编写高质量代码改善程序的157个建议:第87个建议之区分WPF和WinF
??? 今天有时间了,继续《编写高质量代码改善程序的157个建议》的阅读,当我阅读到建议87的时候,里面的一些代码示例和文中所说的不一致了,是不是我现在用的是NetFramework 4.0的缘故,已经把一些问题修复了,今天把问题写下来,告诉大家文中有些小问题需要修复一下。 ?? WPF和WinForm窗体应用程序都有一个要求,那就是UI元素(Button,Label,Textbox控件等)必须由创建它的那个线程来更新。WinForm这方面的限制并不是很严格,所以像下面这样的代码,在Winform中的大部分情况下都可以运行: private void buttonStartAsync_Click(object sender,EventArgs e) { Task t=new Task(()=>{ while(true) { label1.Text=DateTime.Now.ToString(); Thread.Sleep(1000); } }); t.ContinueWith((task)=>{ try { task.Wait(); } catch(AggregateException ex) { Foreach(Exception item in ex.InnerExceptions) { MessageBox.Show(string.Format("异常类型:{0}{1}来自:{2}{3}异常内容:{4}",item.GetType(),Environment.NewLine,item.Source,item.Message)); } } },TaskContinuationOptions.OnlyOnFauled); t.Start(); } 我把这段代码原封不动的有敲了一遍,但是在我的测试实例里面抛出了异常,截图如下: 现在在Winform里面,我测试过的Task和多线程的操作都会报这个错误,修正这个错误很容易,可以在当前类的构造函数里面增加一下一段代码就可以: CheckForIllegalCrossThreadCalls = false;
代码效果截图如下: 现在就好了,程序就可以正常运行了,我的文章【其他信息: 线程间操作无效: 从不是创建控件“控件名”的线程访问它。】可以解决这类问题,有详细解释。 所以说,WPF和WinForm都是严格执行主线程操作UI元素的原则。 ?处理多线程情况下访问UI控件还有很多方法,现在我就在罗列出一下代码: 1 Task t = { 2 while () 3 { 4 if (lblResult.InvokeRequired) 5 { 6 lblResult.BeginInvoke(new Action(() => 7 { 8 lblResult.Text = DateTime.Now.ToString(); 9 })); 10 } 11 else 12 13 lblResult.Text =14 15 Thread.Sleep(); 16 } 17 }); 18 t.ContinueWith((task)=>19 try 20 { 21 task.Wait(); 22 23 (AggregateException ex) 24 25 foreach (var item ex.InnerExceptions) 26 27 MessageBox.Show(28 29 30 },TaskContinuationOptions.OnlyOnFaulted); 31 t.Start(); 我们可以模仿WPF处理线程的方法,增加两个新方法,这两个方法是CheckAccess和VerifyAccess,这两个方法是WPF的UI控件的最终积累DispatcherObject类型中的两个方法,代码如下: 1 namespace System.Windows.Threading 2 3 // 4 // 摘要: 5 表示与 System.Windows.Threading.Dispatcher 关联的对象。 6 public abstract class DispatcherObject { 8 9 10 初始化 System.Windows.Threading.DispatcherObject 类的新实例。 11 protected DispatcherObject(); 12 13 14 15 获取与此 System.Windows.Threading.DispatcherObject 关联的 System.Windows.Threading.Dispatcher。 16 17 返回结果: 18 调度程序。 19 [EditorBrowsable(EditorBrowsableState.Advanced)] 20 public Dispatcher Dispatcher { get; } 21 22 23 24 确定调用线程是否可以访问此 System.Windows.Threading.DispatcherObject。 25 26 27 如果调用线程可以访问此对象,则为 true;否则,为 false。 [EditorBrowsable(EditorBrowsableState.Never)] 29 bool CheckAccess(); 30 31 32 强制调用线程具有此 System.Windows.Threading.DispatcherObject 的访问权限。 33 34 异常: 35 T:System.InvalidOperationException: 36 调用线程不可以访问此 System.Windows.Threading.DispatcherObject。 37 38 void VerifyAccess(); 39 } 40 } 然后,我们给自己的类型加两个类似的方法,完整代码如下: 1 partial Form1 : Form 3 private Thread mainThread; 4 public Form1() { 6 InitializeComponent(); } 8 CheckAccess() 11 return mainThread == Thread.CurrentThread; 13 VerifyAccess() 15 16 if (!CheckAccess()) { 18 throw new InvalidOperationException(调用线程无法访问对象,因为另一个线程拥有此对象! } 21 void button1_Click(23 Task t = 24 25 26 27 28 lblResult.BeginInvoke(30 lblResult.Text =31 32 33 34 35 lblResult.Text =36 37 Thread.Sleep(38 40 t.ContinueWith((task)=>41 42 43 44 45 46 47 48 49 MessageBox.Show(50 51 52 53 t.Start(); 54 55 } 多线程是一个很复杂的话题,我也在学习阶段和总结阶段,有不足的地方,大家多多指教。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |