WPF 异步加载数据

Posted mq0036

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF 异步加载数据相关的知识,希望对你有一定的参考价值。

发个从网上找到代码,供参考吧。

private void Window_Loaded(object sender, RoutedEventArgs e)
{
  var Loads= this.Dispatcher.BeginInvoke(new Action(() =>
  {
  // 这里执行其它耗时操作 
  }));
  Loads.Completed += new EventHandler(Loads_Completed);
}

void Loads_Completed(object sender, EventArgs e)
{
//执行完成后的操作,如隐藏加载提示文字,显示加载完成后的数据等
}

出处:https://blog.csdn.net/ht_zhaoliubin/article/details/45482203

=================================================================================

加载某个界面时,需要获取数据,而数据返回的时间比较长,这个时候可以异步加载界面。

1、在该窗体的加载事件(Load)中编写以下代码:
 new Thread(p=>{DataBinding();}).Start();
2、界面加载时获取数据和绑定数据都写在DataBinding()方法中
private void DataBinding()
{
this.Dispatcher.BeginInvoke(new Action(()=>{
//编写获取数据并显示在界面的代码
}));
}

 

出处:https://www.cnblogs.com/xiaomianyang/p/6416074.html

=================================================================================

前言:以前使用数据库搜数据后直接传到前台设置,后来学会了绑定,界面结合部分总算是有了点经验,But....有时候搜数据没那么快,要等待数据返回的时候界面假死,白屏,很崩溃,后来使用了几种方法 :1.延迟加载先出来界面,在赋值数据 ,2.利用多线程加载,但是一开始的时候会是空白(可以预先初始化来解决),然后搭配complate事件;3.逻辑控制,就是自己想一些办法让程序看起来是正常的,比如加载页,延时,锁死等..;

然而:我今天要讲的是异步加载,并且包含绑定部分的代码;

下面这一部分是 在一个汇总类(class)里 需要收集 值的方法  并且在每一个属性值赋值的时候通知前台页面改变

如果不需要报告进度 一段一段显示 可以直接等最终结果;

不说了上代码;

public async Task MyMethodAsync(IProgress<TaskCallBackValue> progress)
        {
            TaskCallBackValue percentCompleate = new TaskCallBackValue();
            if (progress == null)
            {
                throw new Exception("必须实现报告进度方法!");
            }
 
            await Task.Delay(TimeSpan.FromSeconds(3));//模拟卡顿效果 
            Task<int> T1 = SugarBase.DB.Queryable<property_house_info>().CountAsync(x => x.take_effect != 2);
           
            progress.Report(new TaskCallBackValue { Name = "TotalHouseNumber", Value = T1.Result });
           
 
            await Task.Delay(TimeSpan.FromSeconds(2));//模拟卡顿效果
            Task<int> T2 = SugarBase.DB.Queryable<property_house_info>().CountAsync(x => x.take_effect == 0);
           
            progress.Report(new TaskCallBackValue { Name = "TotalInuseHouseNumber", Value = T2.Result });
        }
 
        public class TaskCallBackValue
        {
            public string Name { get; set; }
            public object Value { get; set; }
 
        }

 

这一段 其实有点类似 BackgroundWork 里的ProgressChanged 事件 这是第一个部分

那么在页面搜索方法中少不了 this.DataContext =this 这样的绑定 

《textBlock  text{binding  Youdata.Data1}.....

《textBlock  text{binding  Youdata.Data2}.....

public void CallMyMethodAsync()
        {
            var progress = new Progress<TaskCallBackValue>();
            progress.ProgressChanged += (sender, e) =>
            {
                Type t = typeof(HomeSummary);
                var Pr = t.GetProperty(e.Name);
                if (Pr != null)
                {
                    Pr.SetValue(CurrentSummaryData, e.Value, null);
                   
//CurrentSummaryData.OnChange(e.Name);//本来想开放PropertyChange事件没想到居然不需要也能触发界面刷新               
 }
 
            };
            MyMethodAsync(progress);
 
        }

 

这个地方就要说一说了,我传出来的是属性名称 和属性的值,但是总不能  class.name1=value ;class.name2=value 这样赋值

所以找了反射的代码利用PropertyInfo的方法给我的汇总类里面的属性赋值 这样 从小方法里发回的异步通知就具有了通用性

 

在前台的效果是 预设界面直接显示无卡顿,  两个TextBlock的值 分别是第3秒和第5秒显示在了界面上

值的提到的是 这个方法里如果有一个出现了 错误那么整个方法都会异常,所以做好异常处理是很有必要的

并且 不能在主线程里await 可能会出现主线程屏蔽初始化,一点界面都没有

综上综上 只是为了 一进界面不卡而已。 

方法摘自 :《C#并发编程经典实例》

 

出处:https://blog.csdn.net/sinat_30224769/article/details/81160142

=================================================================================

如何在WPF中加载大批量数据,并且不会阻塞UI线程,尤其是加载大量图片时,这活儿一直是很多朋友都相当关注的。世上没有最完美的解决之道,咱们但求相对较优的方案。

经过一些试验和对比,老周找到了一种算是不错的方案,重点是这个方案比较简单,无须闯五关斩六将,只要你对数据绑定有些基础就好了。

好,F话少扯,咱们开始吧。

老周手里没有那么多照片,那就用同一张图片做测试吧。假设我要在应用程序运行时加载 2 万张图片,我想2W张应该可以了,没见过谁会傻到要加载100W张那么变态。

大致情况是:数据源集合是一个 ObservableCollection<Uri>, 也就是说集合中放的是图像的URI,为什么不放BitmapSource 呢,因为 DependencyObject 是不能跨线程操作的,只能在UI线程上创建。默认情况下,ObservableCollection<T>也不能在非UI线程上操作,不过,我可以通过调用以下方法来让它可以跨线程操作:

public static void EnableCollectionSynchronization(IEnumerable collection, object lockObject)

这个方法是 BindingOperations 类公开的静态方法,可以在窗口的构造函数中调用它,而且一定要在操作集合之前调用。调用时,把 ObservableCollection 集合传递给 collection 参数,第二个参数lockObject 是一个自定义对象,它指的是可以在线程间同步时引用的对象,在异步代码中,可以把这个对象写在一个 lock 语句块中。主要用途是防止UI访问集合的过程中,集合被其他线程意外修改。

下面代码开启跨线程访问集合支持:

            images = new ObservableCollection<Uri>();
            ……
            lbImages.SetBinding(ItemsControl.ItemsSourceProperty, b);

            // 这一句很关键,开启集合的异步访问支持
            BindingOperations.EnableCollectionSynchronization(images, lockobj);

 

然后在窗口的构造函数中,执行一个新 Task,用一个新线程来加载数据。

技术图片
            Task.Run(() =>
            {
                // 代码写在 lock 块中
                lock (lockobj)
                {
                    for (int i = 0; i < 20000; i++)
                    {
                        Uri u = new Uri("0.jpg", UriKind.Relative);
                        images.Add(u);
                    }
                }
            });
技术图片

开始一个新Task是为了让主线程不受阻止,可以继续响应UI操作。

 

由于集合中都是 URI,而界面上显示的是图像,可以弄一个自定义的数据转换器,转换为位图。

技术图片
    public sealed class UriToBitmapConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            Uri uri = (Uri)value;
            BitmapImage bmp = new BitmapImage();
            bmp.DecodePixelHeight = 250; // 确定解码高度,宽度不同时设置
            bmp.BeginInit();
            // 延迟,必要时创建
            bmp.CreateOptions = BitmapCreateOptions.DelayCreation;
            bmp.CacheOption = BitmapCacheOption.OnLoad;
            bmp.UriSource = uri;
            bmp.EndInit(); //结束初始化
            return bmp;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
技术图片

因为是单向转换,所以ConvertBack就免了。

注意,在实例化BitmapImage时,DecodePixelHeight 和 DecodePixelWidth 属性只能设置任意一个,不要同时设置,不然图片的比例会变形。如果我们界面用的图不需要很大,就设一个小的值,比如200像素,这样可以节约性能。

还可以把 CreateOptions 属性设为 DelayCreation ,这样只在图像需要时才会创建,也省了一些性能。

 

为了让这个转换器能在XAML代码中访问,需要把它的实例声明在UI的资源列表中。

        <Grid.Resources>
            <local:UriToBitmapConverter x:Key="tobmpcvt"/>
        </Grid.Resources>

 

接下来就是用Binding了,实现界面绑定。

技术图片
        <ListBox Name="lbImages" ScrollViewer.IsDeferredScrollingEnabled="False"
                 ScrollViewer.HorizontalScrollBarVisibility="Disabled">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Image Height="200" Width="200" Source="{Binding IsAsync=True,Converter={StaticResource tobmpcvt}}"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
        </ListBox>
技术图片

 

使用 Binding 时,把 IsAsync 属性设为 True,这样允许界面使用辅助线程来绑定数据,记得,记得。

 

这样就完成了,然后我们可以运行,让程序加载 2万个图像。这时候会发现,程序运行后不会卡住了,而且把滚动往下拖动时,会自动加载数据。

技术图片

 

如何?这效果不错吧。

示例源代码下载地址

 

出处:https://www.cnblogs.com/tcjiaan/p/5532104.html

=================================================================================

 

我推荐使用BackgroundWork 进行异步数据下载,然后在完成事件里绑定数据。

以上是关于WPF 异步加载数据的主要内容,如果未能解决你的问题,请参考以下文章

WPF使用异步+绑定的方式处理大数据量

.net深呼吸WPF异步加载大批量图像

当片段视图加载是异步任务的一部分时,如何在片段加载之前显示进度条?

WPF 在异步等待模式中使用 MVVM 和 WCF

当活动中的异步任务完成时如何在片段中重新加载ui?

如何延迟或异步此 WordPress javascript 片段以最后加载以加快页面加载时间?