WPF中用于嵌入其他进程窗口的自定义控件(AppContainer)
在Windows上开发客户端程序的时候,有时候我们希望能将其他进程的窗口嵌入到我们自己的程序窗口中,从视觉效果上看就像是其他进程的窗口时我们自己的程序窗口的一部分。具体的思路是,调用Windows API的SetParent方法,设置外部进程主窗口的父容器设置为我们自己的程序容器句柄。
在Winforms程序中,很容易实现此功能。但是在WPF中会稍微麻烦一点,因为WPF的容器控件是没有自己的独立的句柄的。因此解决思路为先在WPF中嵌入一个Winform的Panel控件(Winform中的Panel控件有自己独立的句柄),然后再将Panel控件的句柄设置为外部程序主窗口的父容器。
为了便于复用,我将相关的功能整理后封装成了一个WPF自定义控件。
一 代码结构
如上图,整个控件的代码结构分为三部分:一是控件的默认模板AppContainer.xaml,二是控件的逻辑控制代码,包括一些对外接口方法的类AppContainer.cs,三是c#调用Win32Api的接口类Win32Api.cs。
二 默认模板
AppContainer的默认模板非常的简单,模板中只有一个WindowsFormsHost控件,此控件用来存放Winform的Panel控件。
<ResourceDictionaryxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:wfi ="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"xmlns:local="clr-namespace:AppContainers"><Style TargetType="{x:Type local:AppContainer}"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type local:AppContainer}"><Border Background="{TemplateBinding Background}"BorderBrush="{TemplateBinding BorderBrush}"BorderThickness="{TemplateBinding BorderThickness}"><Grid><wfi:WindowsFormsHost x:Name="PART_Host"/></Grid></Border></ControlTemplate></Setter.Value></Setter></Style>
</ResourceDictionary>
三 Win32Api
主要用到了Win32Api的SetParent方法来设置被嵌入程序的父容器句柄以及MoveWindow来设置被嵌入程序在容器中的位置。
[DllImport("user32.dll", SetLastError = true)]public static extern int SetParent(IntPtr hWndChild, IntPtr hWndNewParent);[DllImport("user32.dll", SetLastError = true)]public static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
四 逻辑控制
1 控件的初始化
如代码所以,在复写控件的OnApplyTemplate方法的时候,通过GetTemplateChild方法找到模板中的WindowsFormHost控件,当其不为空的时候,实例化Winform的Panel控件,并将其添加到WindowsFormHost中去。
public override void OnApplyTemplate(){base.OnApplyTemplate();_winFormHost = GetTemplateChild("PART_Host") as WindowsFormsHost;if(_winFormHost != null){_hostPanel = new System.Windows.Forms.Panel();_winFormHost.Child = _hostPanel;}}
2 外部窗口的嵌入
外部窗口的嵌入方法有两个:一个是给定程序路径,让控件启动并嵌入程序;一个是当被嵌入程序已经启动时,直接传入已经启动的被嵌程序的进程,然后调用嵌入进程的接口嵌入程序。
启动并嵌入外部进程的方法:
public bool StartAndEmbedProcess(string processPath){bool isStartAndEmbedSuccess = false;_eventDone.Reset();//启动进程_process = StartApp(processPath);if (_process == null){return false;}//确保可获取到句柄Thread thread = new Thread(new ThreadStart(() =>{while (true){if (_process.MainWindowHandle != (IntPtr)0){_eventDone.Set();break;}Thread.Sleep(10);}}));thread.Start();//嵌入进程if (_eventDone.WaitOne(10000)){isStartAndEmbedSuccess = EmbedApp(_process);if (!isStartAndEmbedSuccess){CloseApp(_process);}}return isStartAndEmbedSuccess;}
直接嵌入外部进程的方法:
public bool EmbedExistProcess(Process process){_process = process;return EmbedApp(process);}
嵌入进程的方法:
/// <summary>/// 将外进程嵌入到当前程序/// </summary>/// <param name="process"></param>private bool EmbedApp(Process process){//是否嵌入成功标志,用作返回值bool isEmbedSuccess = false;//外进程句柄IntPtr processHwnd = process.MainWindowHandle;//容器句柄IntPtr panelHwnd = _hostPanel.Handle;if (processHwnd != (IntPtr)0 && panelHwnd != (IntPtr)0){//把本窗口句柄与目标窗口句柄关联起来int setTime = 0;while (!isEmbedSuccess && setTime < 10){isEmbedSuccess = (Win32Api.SetParent(processHwnd, panelHwnd) != 0);Thread.Sleep(100);setTime++;}//设置初始尺寸和位置Win32Api.MoveWindow(_process.MainWindowHandle, 0, 0, (int)ActualWidth, (int)ActualHeight, true);}if(isEmbedSuccess){_embededWindowHandle = _process.MainWindowHandle;}return isEmbedSuccess;}
3 当外部程序放大缩小时,被嵌入程序窗口界面要能跟着改变,所以要复写OnRender方法,在方法中调用MoveWindow方法来设置被嵌程序的初始位置和大小
protected override void OnRender(DrawingContext drawingContext){if (_process != null){Win32Api.MoveWindow(_process.MainWindowHandle, 0, 0, (int)ActualWidth, (int)ActualHeight, true);}base.OnRender(drawingContext);}protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo){this.InvalidateVisual();base.OnRenderSizeChanged(sizeInfo);}
4 当外部程序关闭时,要能同时关闭被嵌入进程。
/// <summary>/// 关闭进程/// </summary>/// <param name="process"></param>private void CloseApp(Process process){if (process != null && !process.HasExited){process.Kill();}}
public void CloseProcess(){CloseApp(_process);}
五 控件的应用
<Window x:Class="WpfAppContainerTest.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:WpfAppContainerTest"xmlns:container="clr-namespace:AppContainers;assembly=AppContainers"mc:Ignorable="d"Title="MainWindow" Height="350" Width="525"><Grid><container:AppContainer x:Name="ctnTest" Margin="20"/></Grid>
</Window>
窗口载入的时候嵌入Windows自带的画图程序。
private void MainWindow_Loaded(object sender, RoutedEventArgs e){if (!_isLoadSuccess){_isLoadSuccess = ctnTest.StartAndEmbedProcess(@"C:\Windows\system32\mspaint.exe");}}
效果图:
源代码
WPF中用于嵌入其他进程窗口的自定义控件(AppContainer)相关推荐
- WPF中的嵌入的资源与Resource
WPF中的嵌入的资源与Resource 我们将资源文件添加至.net C#工程时,文件的生成操作有多种可选方式.通常用的多的是两种:[嵌入的资源]和[Resource],如果从需要从代码中使用这些资源 ...
- Linux下 Qt界面程序嵌入另一个Qt界面程序_Qt应用嵌入外部进程窗口
项目工程的实现,想要使用多个程序进行实现,在里面存在一定的调用的过程:调查的情况如下 Qt界面程序嵌入另一个Qt界面程序[Linux] Qt界面程序嵌入另一个Qt界面程序[Linux]_ptc321的 ...
- windows7/10中Excel以单独进程窗口打开设置方法?
近期因为客户反馈Excel表格每次都在同窗口中,关闭一个窗口后全部都会关闭掉!经过我网上多方苦寻,了解到可以修改注册表来实现以独立进程的方式来打开Excel,特分享出来给大家!此方法经过本人测试,保证 ...
- linux 进程间界面嵌套,WPF 同一窗口内的多线程/多进程 UI(使用 SetParent 嵌入另一个窗口)...
WPF 的 UI 逻辑只在同一个线程中,这是学习 WPF 开发中大家几乎都会学习到的经验.如果希望做不同线程的 UI,大家也会想到使用另一个窗口来实现,让每个窗口拥有自己的 UI 线程.然而,就不能让 ...
- 【原创】A进程窗口嵌入到B进程窗口中显示
做了一个把A应用程序嵌入到B应用程序中显示的Demo,源码: //在B应用启动时创建A进程 CreateProcess(_T("A.exe"),NULL,NULL,NULL,FAL ...
- 【小沐学C#】WPF中嵌入web网页控件(WebBrowser、WebView2、CefSharp)
文章目录 1.简介 1.1 WPF简介 1.2 WPF 体系结构 1.3 WPF入门开发 2.WebBrowser 2.1 WebBrowser特点 2.2 WebBrowser常用的属性.方法和事件 ...
- linux 运行c b停止,以下Linux命令中,用于终止某个进程的命令是()。A.deadB.killC.quitD.exit...
以下Linux命令中,用于终止某个进程的命令是().A.deadB.killC.quitD.exit 更多相关问题 The substance does not dissolve in water _ ...
- c51转汇编语言窗口,在C51语言中如何嵌入汇编语言
keil C 语言中嵌入汇编语言进行混合编程,方法如下: 1.在C 文件中要嵌入汇编代码片以如下方式加入汇编代码 #pragma ASM ; Assembler Code Here #pragma E ...
- WPF中,如何将Vista Aero效果扩展到整个窗口
WPF中,如何将Vista Aero效果扩展到整个窗口 原文:WPF中,如何将Vista Aero效果扩展到整个窗口 WPF中,如何将Vista Aero效果扩展到整个窗口 ...
最新文章
- 九种将元器件从PCB上拆焊下的方法
- java gson使用_Java 中 Gson的使用
- mui 时间样式错乱_微信编辑器样式排版错位怎么回事?
- 统计学第一章--最小二乘拟合正弦函数,正则化
- 大数据可视化技术的挑战及应对
- Demo(3月28日)
- note8 升级android9,明年升!三星七大旗舰集体获安卓9.0认证:这阵容赢了!
- 运行在一个完全隔离环境中的完整计算机系统,凤凰系统完全释放PC性能 模拟器是指通过软件模拟具有完整硬件系统功能、运行在一个完全隔离环境中的计算机系统...
- 电信路由器怎么设置虚拟服务器,电信宽带怎么设置路由器?
- <Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)
- java 汉字拼音首字母_java获取汉字拼音首字母
- js 将16进制转为字符串
- 专插本计算机二级英语四级,专插本考试需要英语四级吗?
- 64位处理器与32位处理器的区别
- Testbench的激励添加和书写技巧
- 苏炳添博士论文研究自己,奥运学术两兼顾
- 联邦学习学习笔记——论文理解《Communication-Efficient Learning of Deep Networks from Decentralized Data》
- AlexNet网络实现
- php微信小程序如何无限点赞,小程序点赞收藏功能
- 修改U8存货档案,提示xml加载错误,可能xml格式不正确
热门文章
- 合工大五套卷_2021森哥五套卷(五)
- python中字符串运算符及用法_python将字符串转换为运算符
- 百度统计 java 实现思路_2019社招阿里、腾讯、蚂蚁金服「四面」Java面试真题分享...
- 手机浏览器推荐_推荐一款手机上最好用的浏览器
- python真的越来越重要吗-为什么现在越来越多人的想学习python,你也是吗,未来到底怎样?...
- mysql io线程异常_mysql主从同步IO线程NO
- adam优化器再理解
- 如何将浮点型准确地转换成字符串
- gridview的sort_GridView排序:SortDirection始终升序
- 字节跳动19春招研发第一次笔试-A卷第一题