WPF的UI更新方式
来源:互联网 发布:如何缩小贫富差距 知乎 编辑:程序博客网 时间:2024/05/16 03:07
WPF的UI更新方式
緣由
在以往的 VB6,或者是 Windows Form 應用程式中,更新 UI的方式極為簡單,往往只是 Application.DoEvents 就可以更新。Windows Form 中更有 Invoke 與 BeginInvoke 可以彈性地使用。
那在 WPF 中,要如何更新 UI 的內容呢?
範例1:Bad Example
當然,要從一個不正確的範例開始。
Ex1Bad.xaml
<Windowx:Class="WpfApplication10.Ex1Bad" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Ex1Bad"Height="300"Width="300"> <StackPanel> <LabelName="lblMsg"Content="Nothing"/> <ButtonContent="Start"Name="btnStart"Click="btnStart_Click"/> </StackPanel></Window>
Ex1Bad.xaml.cs
usingSystem.Threading;usingSystem.Windows; namespaceWpfApplication10{ publicpartial class Ex1Bad : Window { publicEx1Bad() { InitializeComponent(); } privatevoid btnStart_Click(objectsender, RoutedEventArgs e) { lblMsg.Content = "Starting..."; Thread.Sleep(3000);//執行長時間工作 lblMsg.Content = "Doing..."; Thread.Sleep(3000);//執行長時間工作 lblMsg.Content = "Finished..."; } }}
這裡以 Thread.Sleep(3000)讓 UI Thread 睡個3秒鐘,來模擬長時間的工作。
這是個常見的程式碼,但卻是沒有用。在 Windows Form 的 API 中有 Application.DoEvents() 可以呼叫。WPF 中沒有類似的嗎?
範例2:使用Windows Form的 DoEvents
原來,WPF 中雖然沒有類似的 api 可以呼叫,但仍可以直接呼叫 Windows Form 的 Application.DoEvents.當然,需要引用 System.Windows.Forms.dll。
Ex2WinformDoEvents.xaml
usingSystem.Threading;usingSystem.Windows;usingswf = System.Windows.Forms; namespaceWpfApplication10{ publicpartial class Ex2WinformDoEvents : Window { publicEx2WinformDoEvents() { InitializeComponent(); } privatevoid btnStart_Click(objectsender, RoutedEventArgs e) { lblMsg.Content = "Starting..."; swf.Application.DoEvents(); Thread.Sleep(3000);//執行長時間工作 lblMsg.Content = "Doing..."; swf.Application.DoEvents(); Thread.Sleep(3000);//執行長時間工作 lblMsg.Content = "Finished..."; } }}
在更新UI後,呼叫 swf.Application.DoEvents(),就可以更新 UI 了。這樣的方式與之前的 VB6是一模一樣的手法。
範例3:WPF DoEvents
哦?WPF 沒有 DoEvents 可以使用,只能呼叫老前輩Windows Form 的 API 嗎?也不是。在 DispacherFrame 文章中就有sample.
Ex3WPFDoEvents.xaml.cs
usingSystem;usingSystem.Security.Permissions;usingSystem.Threading;usingSystem.Windows;usingSystem.Windows.Threading; namespaceWpfApplication10{ publicpartial class Ex3WPFDoEvents : Window { publicEx3WPFDoEvents() { InitializeComponent(); } privatevoid btnStart_Click(objectsender, RoutedEventArgs e) { lblMsg.Content = "Starting..."; DoEvents(); Thread.Sleep(3000);//執行長時間工作 lblMsg.Content = "Doing..."; DoEvents(); Thread.Sleep(3000);//執行長時間工作 lblMsg.Content = "Finished..."; } [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] publicvoid DoEvents() { var frame = newDispatcherFrame(); Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, newDispatcherOperationCallback(ExitFrame), frame); Dispatcher.PushFrame(frame); } publicobject ExitFrame(objectf) { ((DispatcherFrame)f).Continue = false; returnnull; } publicstatic void DoEvents2() { Action action = delegate{ }; Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Input, action); } }}
DoEvents() 與 DoEvents2() 的效果相同。
DoEvents is Evil
DoEvents 這麼好用,為什麼WPF還要發明 Dispatcher,或 Windows Form 的 BeginInvoke 這些 API 呢?
跑以下的程式碼看看吧。
privatevoid btnEvil_Click(objectsender, RoutedEventArgs e){ for(inti = 0; i < 10000000; i++) { System.Windows.Forms.Application.DoEvents(); } MessageBox.Show("Ok");}
執行時,記得打開工作管理員看看CPU 的負載,會持續飆高一斷時間。雖然 UI 沒有任何的更新,為何 CPU 會飆高呢?
DoEvent 的原理是execution loop,也就是以迴圈的方式來查詢是否有要更新的訊息(message)。一看到迴圈,各位看倌就知道是怎麼回事了吧。
範例3中的WPF 的 DoEvents 也是相同的道理。
範例4:BackgroundWorker
有沒有較正常的方式來更新 UI 呢?看一下Ex1Bad.xaml.cs的設計方式吧。更新lblMessage後執行一段工作,這基本上是同步的寫作方式。在 UI Thread 上執行工作,本來就會使得 UI 停頓,使用者感到不方變。
正確的方式,是使用BackgroundWorker來執行長時間的工作,並以非同步的方式更新在 UI Tread 上的UI內容。
Ex4BackgroundWorker.xaml.cs
usingSystem.ComponentModel;usingSystem.Threading;usingSystem.Windows;usingSystem.Windows.Controls; namespaceWpfApplication10{ publicpartial class Ex4BackgroundWorker : Window { publicEx4BackgroundWorker() { InitializeComponent(); } privatevoid btnStart_Click(objectsender, RoutedEventArgs e) { ExecuteLongTimeWork(lblMsg,"Starting"); ExecuteLongTimeWork(lblMsg,"Doing"); ExecuteLongTimeWork(lblMsg,"Finished"); } privatevoid ExecuteLongTimeWork(Label label, stringmessage) { var backgroundWorker = newBackgroundWorker(); backgroundWorker.DoWork += (s, o) => { Thread.Sleep(3000);//執行長時間工作 }; backgroundWorker.RunWorkerCompleted += (s, args) => { Dispatcher.BeginInvoke(newAction(() => { label.Content = message; })); }; backgroundWorker.RunWorkerAsync(); } }}
更新 UI 時,又使用 Dispatcher 來更新 UI。基本上,Dispatcher 是一個 Queue 的概念,凡是使用 Dispatcher 來更新 UI,WPF 會照 DispatcherPriority 的優先順序來更新 UI。
結論
雖然只是小小的UI 更新方式,也是有不少的學問呢!
- WPF的UI更新方式
- WPF多线程UI更新
- Android更新UI的方式
- 非UI thread更新UI的方式
- 【WPF学习】WPF、WinForm(C#)多线程编程并更新界面(UI)/子线程更新主界面方式
- 在WPF的用户线程中更新UI界面
- 在WPF的用户线程中更新UI界面
- 在wpf的用户线程中更新ui界面
- WPF跨线程更新UI的3种方法
- WPF跨线程更新UI的3种方法
- WPF异步更新UI的两种方法
- Activity更新UI的俩种方式
- Android 更新ui的集中方式
- 更新UI的四种方式
- Android更新UI的五种方式
- Android更新UI的四种方式
- 更新UI的四种方式
- 更新UI的四种方式分析
- 大数乘法和大数除法模板
- LCD驱动实例(二)
- excel 表數據導入到 數據庫表
- VS工具打开高版本的解决方案
- GYP(Generate Your Project)一个很有价值的构建系统
- WPF的UI更新方式
- 闪回(Flashback)
- 晒晒自己刚刚写的大学生创新工作室阶段性总结
- 传感器介绍
- informix错误Could not do a physical-order read to fetch next row解决
- 提升linux下tcp服务器限制
- asp.net导出Excel/Csv格式数据最优方案(C#)
- c中static的作用
- Servelt学习:SQL注入漏洞及其避免方法