拒绝卡顿——在WPF中使用多线程更新UI

原文:拒绝卡顿——在WPF中使用多线程更新UI

有经验的程序员们都知道:不能在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。可以根据实际需要进行相关修改。这里有一个完整的示例,也可以参考一下。

时间: 2024-11-03 01:51:33

拒绝卡顿——在WPF中使用多线程更新UI的相关文章

android使用多线程更新ui示例分享_Android

Android线程涉及的技术有:Handler;Message;MessageQueue;Looper;HandlerThread. 下面看一段在线程中更新UI的代码: 复制代码 代码如下: public class MainActivity extends Activity {private TextView timeLable;private Button stopBtn;private Thread mThread;private boolean isRunning = true;priv

vb.net多线程更新ui问题

问题描述 我百度了下,关于这方面的知识很少,特来请教csdn的各位大牛多线程post请求多个不同网页,然后返回其页面的源码,我的代码如下.遇到的问题:我加入多线程不行...不知道是不是我的代码错了ImportsSystemImportsSystem.IOImportsSystem.NetImportsSystem.TextImportsSystem.ThreadingPublicClassForm1DimiAsStringDimiiAsStringDimtAsThreadPrivateDeleg

eayui-easyui为什么会有卡顿感?

问题描述 easyui为什么会有卡顿感? 用easy ui写完的界面卡顿感很严重,谁知道这是为什么?一般都有哪些原因会导致这些问题 因为界面是复合型页面,也就是主页面里面套子页面 加载子页面的方式是jquery的load方式去加载,当Jquery通过load方式写入html代码的时候 就会出现卡顿的感觉 解决方案 换成easy ui 的方式去传输数据就没有卡顿感了 解决方案二: 看下是不是有非异步的操作,是不是运算量太大,网络连接太慢 解决方案三: 我也感觉easyui慢,是不是页面开头要引入的

WPF多线程出现UI卡顿,求救!!!

问题描述 第一次用WPF,被多线程搞死了.写的代码出现了UI卡顿现象,求解救.多线程是从网上找的代码,和WInform用法,完成不同.是不是写的不对,求帮助.intMaxID=0;intMinID=0;ThreadoldThread;ThreadnewThread;DataTableall=newDataTable();publicMainWindow(){InitializeComponent();//this.Title=GetApp.GetValue("Title");oldTh

主线程阻塞-Android屏幕卡顿、黑屏问题、多线程

问题描述 Android屏幕卡顿.黑屏问题.多线程 4C 我在程序首次启动的时候复制一些文件到sd卡上面,我用的是异步任务来实现的,但是还是明显感到启动时黑屏卡顿很久.按理论来说,启用了异步任务不会阻塞主线程的,但为什么主线程还是被阻塞了,很疑惑 解决方案 上代码看看吧.或者你降优先级试试. 解决方案二: class CopyTask extends AsyncTask{ @Override protected Void doInBackground(String... params) { //

在win7电脑中使用远程桌面出现卡顿怎么办?

  远程桌面,也就是咱们比较熟悉的远程操作,在ghost win7 64位旗舰版下载电脑中,咱们通常会使用这个功能来完成一些本地无法完成的工作,日常的时候,似乎不少用户都喜欢在QQ程序中使用远程桌面,但是实际上,在咱们的win7旗舰版电脑中,也是存在有自带的远程桌面的功能的,大家只需要打开win7旗舰版电脑的开始菜单,然后在下方的搜索窗口中输入远程桌面就可以找到了.但是最近,却有使用win7旗舰版电脑的用户表示,自己创建远程桌面之后会出现操作卡顿的情况,那么这个问题,咱们要如何解决呢?下面,小编

android webview 滑动-webview中内容滑动卡顿

问题描述 webview中内容滑动卡顿 有谁遇到过webview里面内置的可上下滑动的内容在android中滑动很卡顿的问题不?求解 解决方案 http://tieba.baidu.com/p/3201362042

安卓机微信浏览器中CSS动画执行卡顿,怎么解决?

问题描述 安卓机微信浏览器中CSS动画执行卡顿,怎么解决? 移动端web页面,有CSS动画,IOS微信中打开页面,动画执行效果很好,可是安卓机打的页面,CSS动画执行有卡顿,怎么解决 解决方案 我也遇到这个问题了,你的问题解决没

滑动-pinterestlikeadapterview 在android4.3系统中会有卡顿怎么破?

问题描述 pinterestlikeadapterview 在android4.3系统中会有卡顿怎么破? 用pinterestlikeadapterview 实现瀑布流,在android4.3系统中会有滑动卡顿的情况,有遇到过的吗求大神支招