WPF&MVVM线程问题(progressbar为例)
来源:互联网 发布:点胶机编程视频 编辑:程序博客网 时间:2024/06/11 04:57
WPF&MVVM线程问题
别让能力撑不起野心
- WPFMVVM线程问题
- 后台UI线程
- 一般线程交互
- a首次修改引入Dispatcher
- b再次修改引入Task
- c最后修改
- MVVM线程交互
- 源码下载
后台,UI线程
专业解释我就不贴,说说自己的个人愚见,线程有后台,UI(前台)之分,UI元素所使用的线程为UI线程,其他的可以理解为后台线程。
区别:程序要关闭,必须等待UI线程终止,而不用等待后台线程终止。(这个也是为什么有时候我们的界面会卡死,但也关闭不了的原因)
举个例子:
界面上做个按钮ProgressBar,和Button,按钮click设置点击事件:
private void Button_Click(object sender, RoutedEventArgs e) { for(int j = 0; j < 100; j++) { this.progressbar.Value = j; System.Threading.Thread.Sleep(100); } }
按钮点击后,我们会发现窗口卡住了,今天我们就是要处理这样的问题。
一般线程交互
有了上面抛的砖,下面我们继续捡砖(常规WPF的线程交互)。
先说说所使用的技术:
1.Dispatcher(UI),线程调度器;
2.Task.Factory,(线程工厂,底层是线程池);
a.首次修改(引入Dispatcher)
直接上代码了
private void Button_Click(object sender, RoutedEventArgs e) { for (int j = 0; j < 100; j++) {this.progressbar.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background, (Action)delegate () { this.progressbar.Value = j; }); System.Threading.Thread.Sleep(100); } }
这里通过this.progressbar.Dispatcher来获取UI线程的调度器,然后使用异步方法更新UI,
关于Dispatcher的用法可以参考这里Dispatcher
实际上是,这里并没有起任何作用,那么就有疑问:为什么我调用了UI线程更新UI却不起作用。
原因在于,我们这个事件的拥有者是sender,而这里的sender就是Button,UI元素,其实这里本身就已经是UI线程,也是主线程。
b.再次修改(引入Task)
老规矩,先上代码:
private void Button_Click(object sender, RoutedEventArgs e) { Task.Factory.StartNew(() => { for (int j = 0; j < 100; j++) { this.progressbar.Value = j; System.Threading.Thread.Sleep(100); } }); }
这里加入了Task.Factory的用法,关于Task.Factory的用法可以参考这里Task.Factory
编译通过,可是运行时报错,在 this.progressbar.Value = j 时报
“调用线程无法访问此对象,因为另一个线程拥有该对象。”
我们startnew了一个新线程,但是在这个线程中操作UI对象(不是UI线程,这里要注意),对象j传递出错。
c.最后修改
聪明的人应该已经知道接下来就是两者一起运用了:
private void Button_Click(object sender, RoutedEventArgs e) { Task.Factory.StartNew(() => { for (int j = 0; j < 100; j++) { this.progressbar.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background, (Action)delegate () { this.progressbar.Value = j; }); System.Threading.Thread.Sleep(100); } }); }
这里结合使用,具体的意思是:在新线程(后台线程)中进行运行运算(sleep(100)模拟运算),在UI线程进行小更新(progressbar),各司其职,合理分配。
MVVM线程交互
关于mvvm的使用,已经不是新鲜事了,但是系统的mvvm的教程还是不多,而且一般都是边开发边学习得来的知识,包括自己,难免会缺三补四。
关于MVVM模式的说明和使用,个人推荐一下msdn说明和devexpress应用&代码
吐槽的话少说,上硬菜。
1.mvvm的滥用:
为什么这么说,自己之前的一个愚见,通过在ViewModel中设置相关的属性变量,命令command,事件event,然后让View的控件进行绑定,通过后台修改ViewModel变量的属性,实现更新UI。这样一来,界面增加控件,ViewModel增加相应的变量,命令,事件,而不用再View的cs后台进行修改。
初期,这是个还可以接受的做法,到了后期,会发现,这个ViewModel十分的庞大,维系成本增加,要是代码转接,这也是一个需要学习了解的过程。(关于这点,还没有学习到相关的资料,只是自己想象减轻工作的思路,自己也是这么做的,就是界面控件化,界面拆分成细致的控件,让控件独立绑定ViewModel,今天问题不在这里)
2.ViewModel不方便的地方:
ViewModel可以实现绝大部分的界面交互,不过,总会有些奇奇怪怪的功能让你懊恼。比如:按钮实现窗体退出功能,子窗体跳转功能,部分控件的部分属性修改。
这些功能,对于winform老手,没有多少选择,直接在View后台几行代码就实现了,而选择了使用MVVM,让窗体的closed属性bignding,这个还真没有试过吧(其实窗体还没有closed属性,只有closed事件)。
3.MVVM线程交互:
以上两点纯属是自己开发过程中的一些牢骚总结,回来正式话题。
ViewModel中的Command实际上是后台线程,而非UI线程,与click事件之类的事件不同,而bingding的属性都会有一个PropertyChanged的方法通知View,它控件bingding的这个属性改变了,让控件刷新数据,这里实质上是UI线程的交互,相关理论知识还得查看INotifyPropertyChanged接口描述。
现在的问题来了,如果我一个控件,没有实现bingding,但我想操作它的属性,该怎么办,这个就是本章的主要问题,如果你正在做相关的工作,从这里直接看就可以了。
所使用的技术点:
1.EventHandler;
2.Dispatcher(同上描述);
ViewModel的代码:
public event EventHandler CloseProgressBarEvent; public event EventHandler CloseWndAndShowGameView; public ICommand PlusCount { get; set; } ………… public MainViewModel() { ………… this.PlusCount = new DelegateCommand(this.plusCount); ………… } private void plusCount() { ………… var dispatcher = App.Current.MainWindow.Dispatcher; dispatcher.BeginInvoke((Action)delegate () { CloseProgressBarEvent?.Invoke(this, tmpArgs); }); Task.Factory.StartNew(() => { ShowValue = 0; System.Threading.Thread.Sleep(2000); while (ShowValue < 100) { ShowValue += 1; System.Threading.Thread.Sleep(100); tmpArgs._message = $"the program precent is {ShowValue.ToString()}"; dispatcher.BeginInvoke((Action)delegate () { CloseProgressBarEvent?.Invoke(this, tmpArgs); }); } dispatcher.Invoke((Action)delegate () { CloseWndAndShowGameView?.Invoke(this, new EventArgs()); }); }); }
这里关键的代码就是var dispatcher = App.Current.MainWindow.Dispatcher获取当前窗口线程,让窗体线程触发EventHandler的操作,下面来看看EventHandler 的代码:
public MainWindow() { InitializeComponent(); var vm = new MainViewModel(); vm.CloseProgressBarEvent += Vm_CloseProgressBarEvent; vm.CloseWndAndShowGameView += Vm_CloseWndAndShowGameView; ; this.DataContext = vm; } private void Vm_CloseWndAndShowGameView(object sender, System.EventArgs e) { GameView newWnd = new GameView(); newWnd.Show(); this.Close(); } private void Vm_CloseProgressBarEvent(object sender, System.EventArgs e) { MyEventArgs tmp = e as MyEventArgs; this.textBlock.Text = tmp._message; }
大家先别吐槽,这event是有点粗暴,但是,能让你一眼就看懂的代码,也就这样了,对,就是这么简单粗暴。直接的使用this来操作UI界面。
这里也使用到了Task.Factory,Dispatcher的异步,同步方法,这就是我们一般线程交互提到的方法。
源码下载
最后,源码在这里Dispatcher。
最近发现CSDN资源已经不能免费了,在这里补充百度云的链接:Dispatcher。
本文最后修改时间:2017年8月12日16:10:11。
- WPF&MVVM线程问题(progressbar为例)
- wpf解决progressbar更新问题(不用线程)
- wpf mvvm使用问题集锦
- ProgressBar线程更新问题
- wpf mvvm 学习(1)
- WPF笔记(三)MVVM
- WPF+MVVM数据绑定问题集锦
- WPF+MVVM数据绑定问题集锦
- WPF-MVVM
- wpf-mvvm
- wpf mvvm
- WPF MvvM
- WPF与MVVM的实现(一)MVVM简介
- WPF ProgressBar显示进度(一)
- WPF ProgressBar显示进度(二)
- WPF ProgressBar显示进度(三)
- WPF ProgressBar显示进度(四)
- WPF ProgressBar显示进度(五)
- 把排序数组转换为高度最小的二叉搜索树
- 手动获取spring的ApplicationContext和bean对象
- GMIC2017未来零售峰会单场门票开放
- Android Paint API总结和使用方法
- android activity启动模式
- WPF&MVVM线程问题(progressbar为例)
- 在微服务中使用领域事件
- Web容器的作用
- 字符串2
- 一张图看懂AI、机器学习和深度学习的区别
- Android 集成融云通信 部分间断手机弹框 程序停止运行 问题
- 栈与队列
- Docker入门实战
- 在使用 bitvise SSH client时,进行隧道S2C端口映射时,远程主机外网不能访问问题。