ActiViz集成到WPF中的空域问题

文章目录

  • 一、场景
    • 1、WPF控件
    • 2、集成ActiViz或者VTK
  • 二、问题
    • 1、需求
    • 2、空域问题
  • 三、解决方案
    • 1、用WindowsFormsHost包裹住ElementHost,然后将WPF的控件放在ElementHost职中:
    • 2、用Window或者Popup去悬浮
    • 3、使用第三方库Microsoft.DwayneNeed(这也是网上出现较多的答案)
  • 四、最新解决方案
    • 1、步骤
    • 2、需要用到的库
    • 3、关键代码如下:
    • 4、运行结果预览:
    • 5、源码链接
  • 五、总结


一、场景

1、WPF控件

众所周知,由于WPF的底层绘制原理不同,WPF的大多数控件都不具备句柄,具备句柄的也就只有那么几种:Window、Popup、ContextMenu等。

2、集成ActiViz或者VTK

在集成VTK时: 一般选择WindowsFormsHost作为载体,将WinForm具备句柄的控件:如Panel,嵌入WPF布局中。然后将Panel的句柄传给C++。

在集成ActiViz时: 需要以RenderWindowControl来承接三维渲染内容,同样一般选择WindowsFormsHost作为载体,将RenderWindowControl放入其中,然后渲染三维内容。

二、问题

1、需求

如果需求是在三维布局界面,悬浮一些控件,且控件的背景色可以设置成透明。

2、空域问题

你会发现悬浮的WPF控件,始终在WindowsFormsHost之下,也就是说WindowsFormsHost所承载的内容始终置顶。这就是WPF由来已久的空域问题。

三、解决方案

1、用WindowsFormsHost包裹住ElementHost,然后将WPF的控件放在ElementHost职中:

<wf:WindowsFormsHost>
<wf:ElementHost>
<Grid>
</Grid>
</wf:ElementHost>
</wf:WindowsFormsHost>

如此,WPF的控件可置顶。但是会产生一下问题:
①WPF的控件绑定需要重新指定数据源
②较多的WindowsFormsHost会增加界面刷新的负担
③较多的WindowsFormsHost处理不慎的话,会出现严重的内存泄漏问题
④WindowsFormsHost所包裹的WPF控件无法透明背景,影响界面美观

2、用Window或者Popup去悬浮

优点:可悬浮于WindowsFormsHost之上、可透明背景
缺点:
①会产生其他显示问题,处理复杂:Popup永远置顶,在锁屏或者弹窗时,Popup始终在最上方。当然用Windows的API重写Popup,可取消置顶,但任然在不同环境可能有取消置顶失败的情况出现。
②Popup和Window作为单独的窗口,已经不再和主页面为一个整体,在页面切换和页面移动时,需要手动更新Popup,Window的显隐和位置,处理较为麻烦。
③Popup的使用在窗口关闭时,存在内存泄漏问题。

3、使用第三方库Microsoft.DwayneNeed(这也是网上出现较多的答案)

<xmlns:interop=clr-namespace:Microsoft.DwayneNeed.Interop;assembly=Microsoft.DwayneNeed></xmlns>

<airspace:AirspaceDecorator AirspaceMode="Redirect" IsInputRedirectionEnabled="True" IsOutputRedirectionEnabled="True">    
<WindowsFormsHost Name="FormsHost">        
<winform:WebBrowser>    
</WindowsFormsHost></airspace:AirspaceDecorator>

以上方式,的确可以实现控件悬浮置顶,且控件背景透明。但经过本人实测,会出现以下不可控问题:
①在切换界面布局,或者刷新界面时会产生不可控的BUG,难以解决
②刷新界面时,存在严重的性能问题
③占用资源过多,三维界面的鼠标交互异常卡顿

四、最新解决方案

基于最新的ActiViz 9.3的版本,可以自己实现一个RenderWindowControl来解决空域问题。

1、步骤

①将D3D11Image控件封装到控件,通过DirectX 纹理映射来绘制图像
②将D3D11Image控件DirectN获取到系统能支持的控件句柄
③将该句柄设置上下文方式,设置到vtkWin32OpenGLDXRenderWindow中:
RenderWindow.SetD3DDeviceContext§;
④重写鼠标交互

2、需要用到的库

DirectNCore
Kitware.mummy.Runtime
Kitware.VTK
Microsoft.Wpf.Interop.DirectX

3、关键代码如下:

using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using DirectN;
using Kitware.VTK;
using Microsoft.Wpf.Interop.DirectX;

namespace WpfD3D11Interop_Net6._0
{
    internal class MyRenderWindowControl : Image
    {
        public vtkWin32OpenGLDXRenderWindow RenderWindow;

        public vtkRenderWindowInteractor Interactor;

        private bool _readyForRendering;

        private IComObject<ID3D11Device> _device;

        private IComObject<ID3D11Texture2D>? _d3d11Texture;

        private D3D11Image _d3d11ImageSource;

        public MyRenderWindowControl()
        {
            _d3d11ImageSource = new D3D11Image();
            base.Source = (ImageSource)(object)_d3d11ImageSource;
            _d3d11ImageSource.OnRender = new Action<nint, bool>(Render);
            D3D11_CREATE_DEVICE_FLAG val = (D3D11_CREATE_DEVICE_FLAG)32;          
            RenderWindow = vtkWin32OpenGLDXRenderWindow.New();
            RenderWindow.ShowWindowOff();
            RenderWindow.SetColorTextureFormat(87u);
            Extensions.WithComPointer<ID3D11DeviceContext>(ID3D11DeviceExtensions.GetImmediateContext(_device), (Action<IntPtr>)delegate (nint p)
            {
                RenderWindow.SetD3DDeviceContext(p);
            });
            RenderWindow.Initialize();
            RenderWindow.Render();
            RenderWindow.RegisterSharedTexture();
            Interactor = vtkRenderWindowInteractor.New();
            Interactor.EnableRenderOff();
            RenderWindow.SetInteractor(Interactor);
            Interactor.RenderEvt += Interactor_RenderEvt;
            vtkInteractorStyleTrackballCamera interactorStyle = vtkInteractorStyleTrackballCamera.New();
            Interactor.SetInteractorStyle(interactorStyle);
            base.MouseMove += RenderWindowControl_MouseMove;
            base.MouseDown += RenderWindowControl_MouseDown;
            base.MouseUp += RenderWindowControl_MouseUp;
            base.MouseWheel += RenderWindowControl_MouseWheel;
            base.KeyDown += RenderWindowControl_KeyDown;
            base.KeyUp += RenderWindowControl_KeyUp;
            base.Focusable = true;
            base.IsHitTestVisible = true;
            base.Loaded += RenderWindowControl_Loaded;
            base.SizeChanged += RenderWindowControl_SizeChanged;
            CompositionTarget.Rendering += CompositionTarget_Rendering;
        }

        private void RenderWindowControl_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (!base.IsFocused)
            {
                Focus();
            }

            if (e.Delta < 0)
            {
                Interactor.MouseWheelBackwardEvent();
            }
            else if (e.Delta > 0)
            {
                Interactor.MouseWheelForwardEvent();
            }
        }

        private void RenderWindowControl_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Point position = e.GetPosition(this);
            Interactor.SetEventPosition((int)position.X, (int)position.Y);
            CaptureMouse();
            if (!base.IsFocused)
            {
                Focus();
            }

            if (e.ChangedButton == MouseButton.Left)
            {
                Interactor.LeftButtonPressEvent();
            }

            if (e.ChangedButton == MouseButton.Right)
            {
                Interactor.RightButtonPressEvent();
            }

            if (e.ChangedButton == MouseButton.Middle)
            {
                Interactor.MiddleButtonPressEvent();
            }
        }

        private void RenderWindowControl_MouseUp(object sender, MouseButtonEventArgs e)
        {
            Point position = e.GetPosition(this);
            Interactor.SetEventPosition((int)position.X, (int)position.Y);
            ReleaseMouseCapture();
            if (e.ChangedButton == MouseButton.Left)
            {
                Interactor.LeftButtonReleaseEvent();
            }

            if (e.ChangedButton == MouseButton.Right)
            {
                Interactor.RightButtonReleaseEvent();
            }

            if (e.ChangedButton == MouseButton.Middle)
            {
                Interactor.MiddleButtonReleaseEvent();
            }
        }

        private void RenderWindowControl_MouseMove(object sender, MouseEventArgs e)
        {
            Point position = e.GetPosition(this);
            Interactor.SetEventPosition((int)position.X, (int)position.Y);
            Interactor.MouseMoveEvent();
        }

        private void RenderWindowControl_KeyUp(object sender, KeyEventArgs e)
        {
            SetInteractorKeyEventInformation(e);
            Interactor.KeyReleaseEvent();
            e.Handled = true;
        }

        private void RenderWindowControl_KeyDown(object sender, KeyEventArgs e)
        {
            SetInteractorKeyEventInformation(e);
            Interactor.KeyPressEvent();
            string text = e.Key.ToString();
            if (text.Length == 1 && text.All(char.IsLetter))
            {
                Interactor.CharEvent();
            }

            e.Handled = true;
        }

        protected void SetInteractorKeyEventInformation(KeyEventArgs e)
        {
            bool flag = (Keyboard.GetKeyStates(Key.Capital) & KeyStates.Toggled) == KeyStates.Toggled;
            int ctrl = (((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control) ? 1 : 0);
            int num = (((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) ? 1 : 0);
            int altKey = (((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt) ? 1 : 0);
            int repeatcount = (e.IsRepeat ? 1 : 0);
            string text = e.Key.ToString();
            sbyte keycode = 0;
            if (text.Length == 1)
            {
                if (text.All(char.IsLetter) && num == 0 && !flag)
                {
                    text = text.ToLower();
                }

                keycode = (sbyte)text[0];
            }

            Interactor.SetKeyEventInformation(ctrl, num, keycode, repeatcount, text);
            Interactor.SetAltKey(altKey);
        }

        private void Interactor_RenderEvt(vtkObject sender, vtkObjectEventArgs e)
        {
            _readyForRendering = true;
        }

        private void Render(nint surface, bool isNewSurface)
        {
            //IL_001d: Unknown result type (might be due to invalid IL or missing references)
            //IL_0040: Unknown result type (might be due to invalid IL or missing references)
            if (isNewSurface)
            {
                nint num = default(nint);
                ComObject.From<IDXGIResource>(ComObject.QueryObjectInterface<ID3D11Texture2D>((object)ComObject.From<ID3D11Texture2D>((IntPtr)surface, true), true), true).Object.GetSharedHandle(out num);
                object obj = default(object);
                _device.Object.OpenSharedResource((IntPtr)num, typeof(ID3D11Resource).GUID, out obj);
                nint num2 = ComObject.QueryObjectInterface(obj, typeof(ID3D11Texture2D).GUID, true);
                _d3d11Texture = (IComObject<ID3D11Texture2D>?)(object)ComObject.From<ID3D11Texture2D>((IntPtr)num2, true);
                _readyForRendering = true;
            }

            if (_readyForRendering)
            {
                RenderWindow.Lock();
                RenderWindow.Render();
                RenderWindow.Unlock();
                Extensions.WithComPointer<ID3D11Texture2D>(_d3d11Texture, (Action<IntPtr>)delegate (nint p)
                {
                    RenderWindow.BlitToTexture(p, IntPtr.Zero);
                });
            }
        }

        private void CompositionTarget_Rendering(object? sender, EventArgs e)
        {
            _d3d11ImageSource.RequestRender();
            _readyForRendering = false;
        }

        private void RenderWindowControl_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            _readyForRendering = true;
            RenderWindow.SetSize((int)base.ActualWidth, (int)base.ActualHeight);
            _d3d11ImageSource.SetPixelSize((int)base.ActualWidth, (int)base.ActualHeight);
        }

        private void RenderWindowControl_Loaded(object sender, RoutedEventArgs e)
        {
            if (PresentationSource.FromVisual(this) is HwndSource hwndSource)
            {
                _d3d11ImageSource.WindowOwner = hwndSource.Handle;
            }

            RenderWindow.SetSize((int)base.DesiredSize.Width, (int)base.DesiredSize.Height);
            _d3d11ImageSource.SetPixelSize((int)base.DesiredSize.Width, (int)base.DesiredSize.Height);
        }
    }
}

4、运行结果预览:

在这里插入图片描述

5、源码链接

踩坑创作不易,白嫖党勿扰:
https://download.csdn.net/download/weixin_48083386/89480594

五、总结

困扰了C#集成VTK很久的空域问题,终于在ActiViz9.3推出了vtkWin32OpenGLDXRenderWindow类之后,得到解决。其解决原理与QT集成VTK的原理一致,都用到了DirectX绘制,并重写交互。本人亲测,目前无任何负面影响。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/744588.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

springcloud-gateway 路由加载流程

问题 Spring Cloud Gateway版本是2.2.9.RELEASE&#xff0c;原本项目中依赖服务自动发现来自动配置路由到微服务的&#xff0c;但是发现将spring.cloud.gateway.discovery.locator.enabledfalse 启动之后Gateway依然会将所有微服务自动注册到路由中&#xff0c;百思不得其解&a…

NineData和华为云在一起!提供一站式智能数据库DevOps平台

以GuassDB数据库为底座 NineData和华为云一起 为企业提供 一站式智能数据库DevOps平台 帮助开发者 高效、安全地完成 数据库SQL审核 访问控制、敏感数据保护等 日常数据库相关开发任务 NineData 智能数据管理平台 NineData 作为新一代的云原生智能数据管理平台&#xf…

Js逆向爬虫基础篇

这里写自定义目录标题 逆向技巧断点一 、请求入口定位1. 关键字搜索2. 请求堆栈3. hook4. JSON.stringify 二、响应入口定位&#xff1a;1. 关键字搜索2. hook3. JSON.parse 逆向技巧 断点 普通断点 条件断点 日志断点 XHR断点 一 、请求入口定位 1. 关键字搜索 key关…

【因果推断python】57_The Difference-in-Differences 3

目录 3) Enlightenment: A Flexible Functional Form Key Concepts 3) Enlightenment: A Flexible Functional Form 有好消息也有坏消息。首先是好消息&#xff1a;我们已经发现问题与函数形式有关&#xff0c;因此我们可以通过修正函数形式来解决这个问题。也就是说&#xf…

竞赛选题 python+大数据校园卡数据分析

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于yolov5的深度学习车牌识别系统实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;3分 该项目较为新颖&am…

短视频最佳时长:成都柏煜文化传媒有限公司

探索时间与内容之间的完美平衡 成都柏煜文化传媒有限公司 在数字媒体日益繁荣的今天&#xff0c;短视频已成为人们获取信息、娱乐休闲的重要形式。然而&#xff0c;关于短视频的最佳时长&#xff0c;一直是一个备受争议的话题。本文将探讨短视频时长的各种考量因素&#xff0…

基于MATLAB对线阵天线进行道尔夫—切比雪夫加权

相控阵天线——基于MATLAB对线阵进行道尔夫—切比雪夫加权 目录 前言 一、阵列天线的综合 二、道尔夫—切比雪夫综合 三、单元间距的改变对切比雪夫阵列方向图的影响 四、单元数的改变对切比雪夫阵列激励分布的影响 五、副瓣电平SLL对切比雪夫阵列激励幅度的影响 六、副…

深入理解Java中的Collectors(Stream流)

引言 在 Java 的 Stream API 中&#xff0c;Collectors 是一个非常强大的工具类&#xff0c;它提供了许多静态方法&#xff0c;用于将 Stream 的元素收集到集合、字符串或其他类型的结果中。使用 Collectors&#xff0c;我们可以轻松地进行数据聚合和转换操作。 文章目录 引言…

小区业主管理系统

摘 要 随着城市化进程的加速和人口的不断增加&#xff0c;小区的数量也在不断增加。小区作为城市居民居住的主要场所&#xff0c;其管理工作也变得越来越重要。传统的小区业主管理方式存在诸多问题&#xff0c;如信息传递不畅、业务处理效率低下等。因此&#xff0c;开发一个高…

Spring底层原理之FactoryBean Bean工厂 单例对象 多例对象

FactoryBean 在 Spring Framework 中&#xff0c;FactoryBean 是一个用于创建其他 Bean 实例的特殊工厂 Bean。它允许开发者自定义 Bean 的创建逻辑&#xff0c;从而更加灵活地管理和配置 Bean 的实例化过程。 FactoryBean 接口 FactoryBean 接口是 Spring 框架中的一个重要…

启动VMWare虚拟机报错

1. 无法打开内核设备“\\.\VMCIDev\VMX”: 操作成功完成。是否在安装 VMware Workstation 后重新引导? 模块“DevicePowerOn”启动失败。 未能启动虚拟机。 解决办法: 解决办法: 将 Ubuntu 64 位.vmx 找到vmci0.present"TRUE"这行改成 vmci0.present "FAL…

【AI编译器】triton学习:矩阵乘优化

Matrix Multiplication 主要内容&#xff1a; 块级矩阵乘法 多维指针算术 重新编排程序以提升L2缓存命 自动性能调整 Motivations 矩阵乘法是当今高性能计算系统的一个关键组件&#xff0c;在大多数情况下被用于构建硬件。由于该操作特别复杂&#xff0c;因此通常由软件提…

fail2ban自动屏蔽之jumpserver

fail2ban是一款实用软件&#xff0c;可以监视你的系统日志&#xff0c;然后匹配日志的错误信息&#xff08;正则式匹配&#xff09;执行相应的屏蔽动作。 jumpserver是一款开源堡垒机&#xff0c;其拥有一定的防护登录&#xff0c;也可以做登录限制&#xff0c;但是相对于防火…

湖南(用户画像)源点调研 适用于新产品开发的市场调研方法

湖南&#xff08;上市验证调研&#xff09;源点咨询认为&#xff1a;其实市场与用户研究的方法不管都什么花哨的名头&#xff0c;本质上只有两种&#xff1a;定量与定性。而对于新产品的开发最重要的就是掌握好定性的研究方法。 问&#xff1a;对于新产品开发我们面对的是什么…

js如何使得四舍五入的百分比之和为100%

在JavaScript中&#xff0c;如果你想要确保一组四舍五入后的百分比之和严格等于100%&#xff0c;那么你不能直接对每个百分比进行四舍五入&#xff0c;因为四舍五入会引入误差。但是&#xff0c;你可以采用一种策略&#xff0c;即先对所有的百分比进行常规的四舍五入&#xff0…

SNEC天合储能秀:全球首发多元场景一站式工商业储能融合解决方案

6月13日-15日&#xff0c;SNEC2024光伏与智慧能源展在上海隆重举行&#xff0c;来自全球95个国家和地区3000家国内外展商齐聚展会&#xff0c;5000行业专家共话产业发展。致力于成为全球光储智慧能源解决方案的领导者&#xff0c;天合光能&#xff08;展位号&#xff1a;7.2H-E…

线性和二次判别分析

线性判别分析 线性判别分析&#xff08;Linear Discriminant Analysis&#xff0c;LDA&#xff09;亦称 Fisher 判别分析。其基本思想是&#xff1a;将训练样本投影到低维超平面上&#xff0c;使得同类的样例尽可能近&#xff0c;不同类的样例尽可能远。在对新样本进行分类时&…

测试行业,你的未来路在何方?失业之外,暗藏的这个危机更可怕!

目前测试行业现状 近期飞书的大规模裁员&#xff0c;无疑为2024年伊始蒙上了一层阴影。再加上“共享员工”模式的兴起&#xff0c;对于身处互联网行业的从业者来说&#xff0c;无疑是雪上加霜。 此外&#xff0c;延续了2023年的情况&#xff0c;在求职平台如BOSS直聘上&#…

基于Java的宠物领养管理系统【附源码】

摘 要 近些年来&#xff0c;随着科技的飞速发展&#xff0c;互联网的普及逐渐延伸到各行各业中&#xff0c;给人们生活带来了十分的便利&#xff0c;宠物管理系统利用计算机网络实现信息化管理&#xff0c;使整个宠物领养的发展和服务水平有显著提升。 本文拟采用IDEA开发工具…

《编译原理》阅读笔记:p19-p24

《编译原理》学习第 4 天&#xff0c;p19-p24总结&#xff0c;总计 5 页。 一、技术总结 1.grouping of phases 这里谈到分组(group)&#xff0c;那么就会有一个疑问&#xff0c;分组的依据是什么&#xff1f;即根据什么来分组。 (1) front end & back end 编译器包含…