網(wǎng)站首頁 編程語言 正文
有經(jīng)驗的程序員們都知道:不能在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);
}
}
}
當(dāng)我們運行這個程序的時候,就會發(fā)現(xiàn):由于主線程大部分的時間片被占用,無法及時處理系統(tǒng)事件(如鼠標(biāo),鍵盤等輸入),導(dǎo)致程序變得非常卡頓,連拖動窗口都變得不流暢;
如何解決這個問題呢,初學(xué)者可能想到的第一個方法就是新啟一個線程,在線程中執(zhí)行更新:
public UserControl1()
{
textBlock = new TextBlock();
this.Content = textBlock;
ThreadPool.QueueUserWorkItem(_ => updateTime());
}
但很快就會發(fā)現(xiàn)此路不通,因為WPF不允許跨線程訪問程序,此時我們會得到一個:"The calling thread cannot access this object because a different thread owns it."的InvalidOperationException異常
那么該如何解決這一問題呢?通常的做法是把耗時的函數(shù)放在線程池執(zhí)行,然后切回主線程更新UI顯示。前面的updateTime函數(shù)改寫如下:
private async void updateTime()
{
while (true)
{
await Task.Run(() => Thread.Sleep(900));
textBlock.Text = DateTime.Now.ToString();
await Task.Delay(100);
}
}
這種方式能滿足我們的大部分需求。但是,有的操作是比較耗時間的。例如,在多窗口實時監(jiān)控的時候,我們就需要同時多十來個屏幕每秒鐘各進行幾十次的刷新,更新圖像這個操作必須在UI線程上進行,并且它有非常耗時間,此時又會回到最開始的卡頓的情況。
看起來這個問題無法解決,實際上,WPF只是不允許跨線程訪問程序,并非不允許多線程更新界面。我們大可以對每個視頻監(jiān)控窗口單獨其一個獨立的線程,在那個線程中進行更新操作,此時就不會影響到主線程。MSDN上有篇文章介紹了詳細(xì)的操作: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上相關(guān)的解釋,也不算難理解,這里就不多介紹了。最后,再來重構(gòu)一下代碼,把在新線程中創(chuàng)建控件的方式改寫如下:
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();
}
當(dāng)然,我這個函數(shù)多了一些不必要的的限制:容器必須是ContentControl,子元素必須是UIElement。可以根據(jù)實際需要進行相關(guān)修改。這里有一個完整的示例,也可以參考一下。
原文鏈接:https://www.cnblogs.com/TianFang/p/3969430.html
相關(guān)推薦
- 2022-09-23 Pandas多列值合并成一列的實現(xiàn)_python
- 2022-09-06 python?OpenCV的imread不能讀取中文路徑問題及解決_python
- 2022-01-14 path.join()和path.resolve()區(qū)別
- 2022-06-06 element ui表單el-form的label自適應(yīng)寬度
- 2022-02-04 sql語句:查詢結(jié)果保留小數(shù)
- 2022-07-12 Linux虛擬機設(shè)置雙網(wǎng)卡
- 2022-07-14 python修改包導(dǎo)入時搜索路徑的方法_python
- 2023-01-05 Kotlin?object的幾種用法示例詳解_Android
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支