当前位置: 首页 > news >正文

WPF异步UI交互功能的实现方法

前面的文章我们提及过,异步UI的基础实现。基本思路主要是开启新的UI线程,并通过VisualTarget将UI线程上的Visual(即RootVisual)连接到主线程上的UI上即可渲染显示。

但是,之前的实现访问是没有交互能力的,视觉树上的UI并不能实现鼠标事件。那么今天我们就把交互的工作也给完成了。

实现交互能力的核心在于PresentationSource,它是完成交互功能的核心。它提供了交互源,它是一个抽象类,不能直接使用。我们需要通过继承它来实现交互逻辑。

具体代码如下:

 public class VisualTargetPresentationSource : PresentationSource{public VisualTargetPresentationSource(HostVisual hostVisual){_visualTarget = new VisualTarget(hostVisual);}public override Visual RootVisual{get{return _visualTarget.RootVisual;}set{Visual oldRoot = _visualTarget.RootVisual;_visualTarget.RootVisual = value;//此处是关键代码,通过根的时间通知,触发Load事件。通知视觉树,元素已经加载。//元素加载后,IsVisible会变成True,命中测试才能开始工作RootChanged(oldRoot, value);}}public override bool IsDisposed{get{return false;}}protected override CompositionTarget GetCompositionTargetCore(){return _visualTarget;}private VisualTarget _visualTarget;}

有了上述代码,我们的异步UI具备了交互能力。但是此时,我们用鼠标点击元素,依然是不会有动作发生的。

因为我们的UI是在异步线程上,它的功能受到一定的阉割,它并不能直接响应鼠标或者键盘事件。所以我们需要通过VisualHost来做路由事件的传递。

请看完整的示例:

//异步UI容器public class AsyncUIContainer : UIElement{public AsyncUIContainer(){Initialize();}private HostVisual hostVisual;private Grid RootVisual;private DispatcherUIThread dispatcherUIThread;/// <summary>/// /// </summary>private void Initialize(){hostVisual = new HostVisual();dispatcherUIThread = new DispatcherUIThread();dispatcherUIThread.Start();dispatcherUIThread.Dispatcher.Invoke(() =>{RootVisual = new Grid();new VisualTargetPresentationSource(hostVisual){RootVisual = RootVisual};});AddVisualChild(hostVisual);}public void UpdateRootLayout(Action<Grid> action){if (action != null){RootVisual.Dispatcher.Invoke(() =>{action(RootVisual);});}}#region overrideprivate UIElement _hitElement;protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters){RootVisual.Dispatcher.Invoke(() =>{_hitElement = (UIElement)RootVisual.InputHitTest(hitTestParameters.HitPoint);});if (_hitElement != null){return new PointHitTestResult(this, hitTestParameters.HitPoint);}else{return null;}}//此处是用来做路由事件转发,用于触发UI线程上的元素事件protected override void OnMouseDown(MouseButtonEventArgs e){base.OnMouseDown(e);if (_hitElement != null){_hitElement.Dispatcher.Invoke(() => _hitElement.RaiseEvent(e));}}//重写布局系统,让多线程UI元素能正常布局protected override Size MeasureCore(Size availableSize){if (availableSize.Height > 0 && availableSize.Width > 0 && !(double.IsInfinity(availableSize.Height) || double.IsInfinity(availableSize.Width))){RootVisual.Dispatcher.Invoke(() =>{RootVisual.Measure(availableSize);});return availableSize;}else{var minSize = new Size(200, 200);RootVisual.Dispatcher.Invoke(() =>{RootVisual.Measure(minSize);                  });return minSize;}}//重写布局系统,让多线程UI元素能正常布局protected override void ArrangeCore(Rect finalRect){base.ArrangeCore(finalRect);RootVisual.Dispatcher.Invoke(() =>{RootVisual.Arrange(finalRect);});RenderSize = finalRect.Size;}//指定起Visual的数量protected override int VisualChildrenCount => 1;//指定需要渲染的Visual对象protected override Visual GetVisualChild(int index){return hostVisual;}public void Dispose(){dispatcherUIThread?.Dispose();}#endregionpublic class VisualTargetPresentationSource : PresentationSource{public VisualTargetPresentationSource(HostVisual hostVisual){_visualTarget = new VisualTarget(hostVisual);}public override Visual RootVisual{get{return _visualTarget.RootVisual;}set{Visual oldRoot = _visualTarget.RootVisual;_visualTarget.RootVisual = value;RootChanged(oldRoot, value);}}public override bool IsDisposed{get{return false;}}protected override CompositionTarget GetCompositionTargetCore(){return _visualTarget;}private VisualTarget _visualTarget;}}

到这里,你就可以愉快的玩耍了。下期我们再实现异步UI在XAML上的编辑能力,可以在xaml中添加普通的xaml代码一样简单使用。

http://www.lryc.cn/news/492287.html

相关文章:

  • 网络基础 - 地址篇
  • # [Unity] 【游戏开发】Unity开发基础2-Unity脚本编程基础详解
  • Milvus实操
  • 35 基于单片机的精确电压表DA-AD转换
  • JDBC 设置 PostgreSQL 查询中 any(?) 的参数
  • 【11-20期】Java面试进阶:深入解析核心问题与实战案例
  • C++——内存池_2
  • 如何使用PHP爬虫获取店铺详情:一篇详尽指南
  • HTML5和CSS3新增特性
  • linux运行vue编译后的项目
  • 论文阅读:A Software Platform for Manipulating theCamera Imaging Pipeline
  • 【MySQL篇】持久化和非持久化统计信息的深度剖析(第一篇,总共六篇)
  • Ubuntu下安装Qt
  • 丹摩征文活动 | AI创新之路,DAMODEL助你一臂之力GPU
  • 数据库(总结自小林coding)|索引失效的场景、慢查询、原因及如何优化?undo log、redo log、binlog 作用、MySQL和Redis的区别
  • Docker容器运行CentOS镜像,执行yum命令提示“Failed to set locale, defaulting to C.UTF-8”
  • OpenCV基本图像处理操作(六)——直方图与模版匹配
  • 【LLM学习笔记】第四篇:模型压缩方法——量化、剪枝、蒸馏、分解
  • python3 自动更新的缓存类
  • 英语知识网站开发:Spring Boot框架应用
  • 文件上传upload-labs-docker通关
  • git(Linux)
  • Doris实战—构建日志存储与分析平台
  • 【vue3+Typescript】unapp+stompsj模式下替代plus-websocket的封装模块
  • Tcon技术和Tconless技术介绍
  • C#-利用反射自动绑定请求标志类和具体执行命令类
  • 高中数学练习:初探均值换元法
  • 数据结构单链表,顺序表,广义表,多重链表,堆栈的学习
  • 【保姆级教程】使用lora微调LLM并在truthfulQA数据集评估(Part 2.在truthfulQA上评估LLM)
  • thinkphp中对请求封装