在WPF桌面应用程序开发过程中,有时候需要将其他程序结合到一起,让他看起来是一个程序,就需要把其他程序的窗口,作为子窗体,嵌入到程序中去。如果都是自己程序,可以将其他程序的项目直接导入引用。
在以下几种情况,可能无法直接修改和调用源程序。
这种时候就只能通过直接将打包的exe程序嵌入到当前程序中去。
需要调用Windows API的SetParent
和MoveWindow
,通过DllImport将API加载进来
SetParent
通过句柄将一个窗体设为另一个窗体的父窗体。
MoveWindow
改变指定窗口的位置和大小.对基窗口来说,位置和大小取决于屏幕的左上角;对子窗口来说,位置和大小取决于父窗口客户区的左上角.对于Owned窗口,位置和大小取决于屏幕左上角.
[DllImport("user32.dll", SetLastError = true)]
public static extern long 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);
使用Process
运行外部程序(以画图程序为示例),需要将外部程序设置为正常的窗体样式,最大化状态的窗体无法嵌入。
var exeName = "C:\\WINDOWS\\system32\\mspaint";
//使用Process运行程序
Process p = new Process();
p.StartInfo.FileName = exeName;
p.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
p.Start();
循环判断运行的外部程序窗体句柄,如果不为零就说明程序已经运行,获取他的句柄。
WPF中控件无法获取句柄,只能获取窗体的句柄,使用WindowInteropHelper
获取WPF的窗体句柄
while (p.MainWindowHandle.ToInt32() == 0)
{
System.Threading.Thread.Sleep(100);
}
IntPtr appWin = p.MainWindowHandle;
IntPtr hwnd = new WindowInteropHelper(this).Handle;
使用 SetParent
将外部程序窗体嵌入当前窗体
SetParent(appWin, hwnd);
效果如下:
窗体已经嵌入称为当前窗体的子窗体,可以跟随移动,子窗体也无法移出父窗体位置。
嵌入窗体之后,子窗体位置可以随机放置,我们可以通过MoveWindow
来设置子窗体的位置和大小
MoveWindow(appWin, 0, 0, 500, 400, true);
完整实现的代码:
var exeName = "C:\\WINDOWS\\system32\\mspaint";//嵌入程序路径,可以改成其他程序
//使用Process运行程序
Process p = new Process();
p.StartInfo.FileName = exeName;
p.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
p.Start();
//获取窗体句柄
while (p.MainWindowHandle.ToInt32() == 0)
{
System.Threading.Thread.Sleep(100);
}
IntPtr appWin = p.MainWindowHandle;//子窗体(外部程序)句柄
IntPtr hwnd = new WindowInteropHelper(this).Handle;//当前窗体(主程序)句柄
//设置父窗体(实现窗体嵌入)
SetParent(appWin, hwnd);
//设置窗体位置和大小
MoveWindow(appWin, 0, 0, 500, 400, true);
后续更新:
WindowsFormsHost
在WPF中调用winform的控件来解决控件没有句柄问题,进行封装控件,解决问题1和2;