解决卡顿——在WPF中使用多线程更新UI
来源:互联网 发布:java接收微信事件推送 编辑:程序博客网 时间:2024/05/22 13:18
解决卡顿——在WPF中使用多线程更新UI
有经验的程序员们都知道:不能在UI线程上进行耗时操作,那样会造成界面卡顿,如下就是一个简单的示例:
publicpartial class MainWindow :Window
{
public MainWindow()
{
InitializeComponent();
this.Dispatcher.Invoke(newAction(()=> { }));
this.Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender,RoutedEventArgs e)
{
this.Content = new UserControl1();
}
}
classUserControl1 : UserControl
{
TextBlock textBlock;
public UserControl1()
{
textBlock = new TextBlock();
this.Content = textBlock;
this.Dispatcher.BeginInvoke(newAction(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函数改写如下:
privateasync void updateTime()
{
while (true)
{
awaitTask.Run(() => Thread.Sleep(900));
textBlock.Text = DateTime.Now.ToString();
await Task.Delay(100);
}
}
这种方式能满足我们的大部分需求。但是,有的操作是比较耗时间的。例如,在多窗口实时监控的时候,我们就需要同时多十来个屏幕每秒钟各进行几十次的刷新,更新图像这个操作必须在UI线程上进行,并且它有非常耗时间,此时又会回到最开始的卡顿的情况。
看起来这个问题无法解决,实际上,WPF只是不允许跨线程访问程序,并非不允许多线程更新界面。我们大可以对每个视频监控窗口单独其一个独立的线程,在那个线程中进行更新操作,此时就不会影响到主线程。MSDN上有篇文章介绍了详细的操作:Multithreaded UI: HostVisual。用这种方式将原来的程序改写如下:
privatevoid MainWindow_Loaded(object sender,RoutedEventArgs e)
{
HostVisual hostVisual =new HostVisual();
UIElement content =new VisualHost(hostVisual);
this.Content = content;
Thread thread =new Thread(newThreadStart(() =>
{
VisualTarget visualTarget =new VisualTarget(hostVisual);
var control = new UserControl1();
control.Arrange(newRect(newPoint(), 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上相关的解释,也不算难理解,这里就不多介绍了。最后,再来重构一下代码,把在新线程中创建控件的方式改写如下:
privatevoid 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(newThreadStart(() =>
{
VisualTarget visualTarget =new VisualTarget(hostVisual);
var control = new T();
control.Arrange(newRect(newPoint(), content.RenderSize));
visualTarget.RootVisual = control;
System.Windows.Threading.Dispatcher.Run();
}));
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
}
当然,我这个函数多了一些不必要的的限制:容器必须是ContentControl,子元素必须是UIElement。可以根据实际需要进行相关修改。这里有一个完整的示例,也可以参考一下。
转自:http://www.cnblogs.com/TianFang/p/3969430.html
- 解决卡顿——在WPF中使用多线程更新UI
- <转载>拒绝卡顿——在WPF中使用多线程更新UI
- 拒绝卡顿——在WPF中使用多线程更新UI
- WPF多线程UI更新
- WPF多线程UI更新——两种方法
- WPF多线程UI更新——两种方法
- WPF多线程UI更新——两种方法
- C# Winform使用线程,委托定时更新界面UI控件,解决界面卡顿问题
- 在WPF的用户线程中更新UI界面
- 在WPF的用户线程中更新UI界面
- 在wpf的用户线程中更新ui界面
- WPf中多线程改UI
- 在WPF程序中使用多线程技术
- android解决:使用多线程和Handler同步更新UI
- WPF使用多线程更新UI界面 涉及资源访问需要使用委托处理
- WPF中UI及时更新,如何在处理长时间工作时,保持界面的持续更新
- WPF中UI及时更新,如何在处理长时间工作时,保持界面的持续更新
- WPF中使用UI线程
- Oracle(5)使用profile文件对口令进行管理
- Java实现字符与Unicode互转
- H
- Object类中clone()方法的修饰符为什么是protected
- 《易读》一个模仿网易云音乐UI,Rxjava+Retrofit+dagger2+MVP的开源项目
- 解决卡顿——在WPF中使用多线程更新UI
- AIDL实现进程间通信
- 转:提高系统性能的相关技术
- 利用Document读取和修改数据
- PyTorch 设置使用GPU
- 1x1卷积核作用
- android-支持多种屏幕[访问资源] 十五
- VTK6 error:no override found for 'vtkRenderWindow'
- 多个线程调用静态方法,是否会出现并发问题?