专栏首页林德熙的博客WPF 跨线程 UI 的方法

WPF 跨线程 UI 的方法

本文告诉大家如何在 WPF 使用多线程的 UI 的方法 在很多的时候都是使用单线程的 UI 但是有时候需要做到一个线程完全处理一个耗时的界面就需要将这个线程作为另一个 UI 线程

在 WPF 可以使用 VisualTarget 做到多个 UI 线程的绘制,注意这里的 WPF 的渲染线程只有一个,多个 UI 线程无法让渲染的速度加快。如果一个界面有很多的 Visual 那么渲染速度也不会因为添加 UI 线程用的时间比原来少

在 WPF 的 VisualTarget 可以用来连接多个不同的线程的 UI 元素,在使用的时候只需要创建,然后在另一个 UI 线程将创建的元素添加到 RootVisual 就可以

           var thread = new Thread(() =>
            {
                _visualTarget = new VisualTarget(xx);

                _visualTarget.RootVisual = 创建的 Visual;
            });

创建一个 VisualTarget 需要用到 HostVisual 通过 HostVisual 可以在多个线程连到视觉树,所以创建 HostVisual 需要在主线程

public MainWindow()
{
	InitializeComponent();

    var hostVisual = new HostVisual();
          
            var thread = new Thread(() =>
            {
                _visualTarget = new VisualTarget(hostVisual);
                

                _visualTarget.RootVisual = 创建的 Visual;
            });
}

这时还需要将 hostVisual 加入视觉树,因为 HostVisual 也是 Visual 最简单将 Visual 加入视觉树的方法是创建一个类继承 UIElement 的方法,请看下面代码

    public class DispatcherContainer : UIElement
    {
        /// <inheritdoc />
        protected override Visual GetVisualChild(int index)
        {
            return _hostVisual;
        }

        /// <inheritdoc />
        protected override int VisualChildrenCount => 1;

        private readonly HostVisual _hostVisual = new HostVisual();
    }

然后在构造函数添加一个线程用来创建另一个 UI 线程,创建一个 UI 线程的最简单方法是运行 Dispatcher.Run() 和设置线程 STA 才可以,注意这里的 Dispatcher 是静态类

            var thread = new Thread(() =>
            {

                System.Windows.Threading.Dispatcher.Run();
            });

            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();

在这个线程里添加 VisualTarget 请看下面

            var thread = new Thread(() =>
            {
                _visualTarget = new VisualTarget(_hostVisual);

                _visualTarget.RootVisual = 创建的元素;

                System.Windows.Threading.Dispatcher.Run();
            });

            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();

下面创建一个简单的元素在另一个线程

            var thread = new Thread(() =>
            {
                _visualTarget = new VisualTarget(_hostVisual);
                DrawingVisual drawingVisual = new DrawingVisual();
                var drawing = drawingVisual.RenderOpen();
                using (drawing)
                {
                    var text = new FormattedText("欢迎访问我博客 http://lindexi.gitee.io 里面有大量 UWP WPF 博客",
                        CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
                        new Typeface(new FontFamily("微软雅黑"), new FontStyle(), FontWeight.FromOpenTypeWeight(1),
                            FontStretch.FromOpenTypeStretch(1)), 20, Brushes.DarkSlateBlue);

                    drawing.DrawText(text, new Point(100, 100));
                }

                var containerVisual = new ContainerVisual();

                containerVisual.Children.Add(drawingVisual);

                _visualTarget.RootVisual = containerVisual;

                System.Windows.Threading.Dispatcher.Run();
            });

            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();

这时的 DispatcherContainer 类看起来是这样

    public class DispatcherContainer : UIElement
    {
        /// <inheritdoc />
        public DispatcherContainer()
        {
            var thread = new Thread(() =>
            {
                _visualTarget = new VisualTarget(_hostVisual);
                DrawingVisual drawingVisual = new DrawingVisual();
                var drawing = drawingVisual.RenderOpen();
                using (drawing)
                {
                    var text = new FormattedText("欢迎访问我博客 http://lindexi.gitee.io 里面有大量 UWP WPF 博客",
                        CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
                        new Typeface(new FontFamily("微软雅黑"), new FontStyle(), FontWeight.FromOpenTypeWeight(1),
                            FontStretch.FromOpenTypeStretch(1)), 20, Brushes.DarkSlateBlue);

                    drawing.DrawText(text, new Point(100, 100));
                }

                var containerVisual = new ContainerVisual();

                containerVisual.Children.Add(drawingVisual);

                _visualTarget.RootVisual = containerVisual;

                System.Windows.Threading.Dispatcher.Run();
            });

            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
        }

        /// <inheritdoc />
        protected override Visual GetVisualChild(int index)
        {
            return _hostVisual;
        }

        /// <inheritdoc />
        protected override int VisualChildrenCount => 1;

        private readonly HostVisual _hostVisual = new HostVisual();
        private VisualTarget _visualTarget;
    }

为了显示元素,需要添加到界面,打开界面添加下面代码

        <local:DispatcherContainer></local:DispatcherContainer>

运行可以看到下面界面,这里的文字是在另一个线程绘制,但是也是和主界面在相同的线程渲染

代码请看 https://github.com/lindexi/UWP/tree/master/wpf/CaitrairSodeyatarFowfurur

更多博客请看 WPF 同一窗口内的多线程 UI(VisualTarget) - walterlv


本文会经常更新,请阅读原文: https://lindexi.gitee.io/post/WPF-%E8%B7%A8%E7%BA%BF%E7%A8%8B-UI-%E7%9A%84%E6%96%B9%E6%B3%95.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接: https://lindexi.gitee.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • WPF 动画实战 点击时显示圆圈淡出效果

    本文告诉大家一个有趣的动画,在鼠标点击的时候,在点击所在的点显示一个圆圈,然后这个圆圈做动画变大,但是颜色变淡的效果。本文的控件可以让大家将对应的容器放在自己应...

    林德熙
  • win10 uwp 获得缩略图 文件缩略图视频小图

    有时候需要获得文件或视频的缩略图。 本文提供两个方法,用于获得文件的缩略图和截取视频指定时间的显示图片。

    林德熙
  • WPF 启动屏幕键盘

    在 Windows 的平板模式下才能自动在获取键盘输入焦点时弹出屏幕键盘,但是 Windows 的屏幕键盘做的粗糙,有时候不会自动开启屏幕键盘,此时需要使用代码...

    林德熙
  • OpenLayers3基础教程——OL3 介绍control

    相比较Ol2的control,OL3显得特别少,下图分别为Ol2和Ol3的control:

    lzugis
  • 细说shiro之六:session管理

    我们先来看一下shiro中关于Session和Session Manager的类图。

    2Simple
  • 点图层叠加与事件响应

    用过百度地图的童鞋一定很羡慕百度地图POi的展示,地图切片+事件响应,以前一直在考虑这个问题,今天,将我的思考结果做一个汇报给大家。下面,将我的实现思路说明一下...

    lzugis
  • 你知道Android Nougat (牛轧糖)有哪些新鲜口味吗?

    Android 7.0 经过5个开发者预览版本的改善,终于在8.22日正式推送,并确定版本名为Nougat(牛轧糖)。结合本人的体验,在此简单的聊聊Andr...

    open
  • 五分钟带你感受RxJava的优雅

    这是一篇RxJava的入门级介绍和接入教程,看完这篇文章你会明白几个问题, · RxJava 是什么 · 它的优势在哪里 · 怎么应用到项目中

    PhoenixZheng
  • winform treeView 数据绑定

    跟着阿笨一起玩NET
  • html基本标签(慕课网)

    听着music睡

扫码关注云+社区

领取腾讯云代金券