<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
有經驗的程式設計師們都知道:不能在UI執行緒上進行耗時操作,那樣會造成介面卡頓,如下就是一個簡單的範例:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.Dispatcher.Invoke(new Action(()=> { })); this.Loaded += MainWindow_Loaded; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { this.Content = new UserControl1(); } } class UserControl1 : UserControl { TextBlock textBlock; public UserControl1() { textBlock = new TextBlock(); this.Content = textBlock; this.Dispatcher.BeginInvoke(new Action(updateTime), null); } private async void updateTime() { while (true) { Thread.Sleep(900); //模擬耗時操作 textBlock.Text = DateTime.Now.ToString(); await Task.Delay(100); } } }
當我們執行這個程式的時候,就會發現:由於主執行緒大部分的時間片被佔用,無法及時處理系統事件(如滑鼠,鍵盤等輸入),導致程式變得非常卡頓,連拖動視窗都變得不流暢;
如何解決這個問題呢,初學者可能想到的第一個方法就是新啟一個執行緒,線上程中執行更新:
public UserControl1() { textBlock = new TextBlock(); this.Content = textBlock; ThreadPool.QueueUserWorkItem(_ => updateTime()); }
但很快就會發現此路不通,因為WPF不允許跨執行緒存取程式,此時我們會得到一個:"The calling thread cannot access this object because a different thread owns it."的InvalidOperationException異常
那麼該如何解決這一問題呢?通常的做法是把耗時的函數放線上程池執行,然後切回主執行緒更新UI顯示。前面的updateTime函數改寫如下:
private async void updateTime() { while (true) { await Task.Run(() => Thread.Sleep(900)); textBlock.Text = DateTime.Now.ToString(); await Task.Delay(100); } }
這種方式能滿足我們的大部分需求。但是,有的操作是比較耗時間的。例如,在多視窗實時監控的時候,我們就需要同時多十來個螢幕每秒鐘各進行幾十次的重新整理,更新影象這個操作必須在UI執行緒上進行,並且它有非常耗時間,此時又會回到最開始的卡頓的情況。
看起來這個問題無法解決,實際上,WPF只是不允許跨執行緒存取程式,並非不允許多執行緒更新介面。我們大可以對每個視訊監控視窗單獨其一個獨立的執行緒,在那個執行緒中進行更新操作,此時就不會影響到主執行緒。MSDN上有篇文章介紹了詳細的操作:Multithreaded UI: HostVisual。用這種方式將原來的程式改寫如下:
private void MainWindow_Loaded(object sender, RoutedEventArgs e) { HostVisual hostVisual = new HostVisual(); UIElement content = new VisualHost(hostVisual); this.Content = content; Thread thread = new Thread(new ThreadStart(() => { VisualTarget visualTarget = new VisualTarget(hostVisual); var control = new UserControl1(); control.Arrange(new Rect(new Point(), content.RenderSize)); visualTarget.RootVisual = control; System.Windows.Threading.Dispatcher.Run(); })); thread.SetApartmentState(ApartmentState.STA); thread.IsBackground = true; thread.Start(); } public class VisualHost : FrameworkElement { Visual child; public VisualHost(Visual child) { if (child == null) throw new ArgumentException("child"); this.child = child; AddVisualChild(child); } protected override Visual GetVisualChild(int index) { return (index == 0) ? child : null; } protected override int VisualChildrenCount { get { return 1; } } }
這個裡面用來了兩個新的類:HostVisual、VisualTarget。以及自己寫的一個VisualHost。MSDN上相關的解釋,也不算難理解,這裡就不多介紹了。最後,再來重構一下程式碼,把在新執行緒中建立控制元件的方式改寫如下:
private void MainWindow_Loaded(object sender, RoutedEventArgs e) { createChildInNewThread<UserControl1>(this); } void createChildInNewThread<T>(ContentControl container) where T : UIElement , new() { HostVisual hostVisual = new HostVisual(); UIElement content = new VisualHost(hostVisual); container.Content = content; Thread thread = new Thread(new ThreadStart(() => { VisualTarget visualTarget = new VisualTarget(hostVisual); var control = new T(); control.Arrange(new Rect(new Point(), content.RenderSize)); visualTarget.RootVisual = control; System.Windows.Threading.Dispatcher.Run(); })); thread.SetApartmentState(ApartmentState.STA); thread.IsBackground = true; thread.Start(); }
當然,我這個函數多了一些不必要的的限制:容器必須是ContentControl,子元素必須是UIElement。可以根據實際需要進行相關修改。這裡有一個完整的範例,也可以參考一下。
到此這篇關於WPF使用多執行緒更新UI的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45