WPF多线程更新UI的一个解决途径

那么该如何解决这一问题呢?通常的做法是把耗时的函数放在线程池执行,然后切回主线程更新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。用这种方式将原来的程序改写如下:

 e)
    {
        ();

        (hostVisual);
        .Content = content;

        (() =>
        {
            (hostVisual);
            ();
            control.Arrange((), content.RenderSize));
            visualTarget.RootVisual = control;

            System.Windows.Threading..Run();

        }));

        thread.SetApartmentState(.STA);
        thread.IsBackground = ;
        thread.Start();
    }

    
    {
         child;

         child)
        {
            )
                );

            .child = child;
            AddVisualChild(child);
        }

         index)
        {
            ;
        }

         VisualChildrenCount
        {
             1; }
        }
    }

这个里面用来了两个新的类:HostVisual、VisualTarget。以及自己写的一个VisualHost。MSDN上相关的解释,也不算难理解,这里就不多介绍了。最后,再来重构一下代码,把在新线程中创建控件的方式改写如下:

 e)
    {
        createChildInNewThread<);
    }

     container) 
        ()
    {
        ();

        (hostVisual);
        container.Content = content;

        (() =>
        {
            (hostVisual);

            ();
            control.Arrange((), content.RenderSize));

            visualTarget.RootVisual = control;
            System.Windows.Threading..Run();

        }));

        thread.SetApartmentState(.STA);
        thread.IsBackground = ;
        thread.Start();
    }

当然,我这个函数多了一些不必要的的限制:容器必须是ContentControl,子元素必须是UIElement。可以根据实际需要进行相关修改。

posted @ 2019-07-24 17:08  卖雨伞的小男孩  阅读(1377)  评论(0编辑  收藏  举报