c#嵌入第三方 窗体

本文介绍了如何使用C#和WPF技术改进Python应用的前端界面,通过C#启动Python程序并将其窗口嵌入到WPFPanel中,实现界面美化和自适应,同时处理了可能出现的问题如无标题窗口的识别和容器大小变化时的适配。
摘要由CSDN通过智能技术生成

项目背景:公司需要为一个Python和matlab应用做一个前端界面,这个Python应用没有界面,只会弹出一个窗体,窗体里面播放视频,非常简陋,我考虑到WPF做界面比较美观,所以选择用C#启动这个Python程序后,把他弹出的窗口嵌入到WPF的界面的一个Panel当中,实现起来还是很简单的,只要知道这个窗体的名称即可。

以下为代码,SetWindow类为窗体设置类,对窗体的设置都在其中完成。

public static class SetWindow
{
    public static IntPtr intPtr;         //第三方应用窗口的句柄
    
    /// <summary>
    /// 调整第三方应用窗体大小
    /// </summary>
    public static void ResizeWindow()
    {
        ShowWindow(intPtr, 0);  //先将窗口隐藏
        ShowWindow(intPtr, 3);  //再将窗口最大化,可以让第三方窗口自适应容器的大小
    }

    /// <summary>
    /// 循环查找第三方窗体
    /// </summary>
    /// <returns></returns>
    public static bool FindWindow(string formName)
    {
        for (int i = 0; i < 100; i++)
        {
            //按照窗口标题查找Python窗口
            IntPtr vHandle = FindWindow(null, formName);     
            if (vHandle == IntPtr.Zero)
            {
                Thread.Sleep(100);  //每100ms查找一次,直到找到,最多查找10s
                continue;
            }
            else      //找到返回True
            {
                intPtr = vHandle;
                return true;
            }
        }
        intPtr = IntPtr.Zero;
        return false;
    }


    /// <summary>
    /// 将第三方窗体嵌入到容器内
    /// </summary>
    /// <param name="hWndNewParent">父容器句柄</param>
    /// <param name="windowName">窗体名</param>
    public static void SetParent(IntPtr hWndNewParent,string windowName)
    {
        ShowWindow(intPtr , 0);                 //先将窗体隐藏,防止出现闪烁
        SetParent(intPtr , hWndNewParent);      //将第三方窗体嵌入父容器                    
        Thread.Sleep(100);                      //略加延时
        ShowWindow(intPtr , 3);                 //让第三方窗体在容器中最大化显示
        RemoveWindowTitle(intPtr );             // 去除窗体标题
    }


    /// <summary>
    /// 去除窗体标题
    /// </summary>
    /// <param name="vHandle">窗口句柄</param>
    public static void RemoveWindowTitle(IntPtr vHandle)
    {
        long style = GetWindowLong(vHandle, -16);                   
        style &= ~0x00C00000;
        SetWindowLong(vHandle, -16, style);
    }


    #region API 需要using System.Runtime.InteropServices;

    [DllImport("user32.dll ", EntryPoint = "SetParent")]
    private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);   //将外部窗体嵌入程序

    [DllImport("user32.dll")]
    public static extern IntPtr FindWindow(string lpszClass, string lpszWindow);      //按照窗体类名或窗体标题查找窗体

    [DllImport("user32.dll", EntryPoint = "ShowWindow", CharSet = CharSet.Auto)]
    private static extern int ShowWindow(IntPtr hwnd, int nCmdShow);                  //设置窗体属性

    [DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]
    public static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, long dwNewLong);

    [DllImport("user32.dll", EntryPoint = "GetWindowLong", CharSet = CharSet.Auto)]
    public static extern long GetWindowLong(IntPtr hWnd, int nIndex);

    #endregion 

}
前端界面调用代码如下,启动第三方应用我是用的Process,代码就不再写了,这里着重介绍窗体的设置。

//先启动第三方应用

//开线程来查找窗体,不然UI线程会卡死

Task.Run(() => {
//PCR TempControl是要查找的窗体名称,自行替换
if (SetWindow.FindWindow(“PCR TempControl”))
{
//这里是WPF的写法,Winform把this.Dispatcher.Invoke改为this.Invoke即可
this.Dispatcher.Invoke(new Action(() =>
{
SetWindow.SetParent(panel1.Handle, “PCR TempControl”); //设置父容器
}));
}
else
{
MessageBox.Show(“未能查找到窗体”);
}
});
在此就完成了第三方窗体的查找,嵌入,以及显示功能,如果你的Panel大小不会跟随窗体变化的话,那么就不要再用下面的代码了,虽然我已尽力优化但下面的代码还是不可避免会出现少量闪烁。

如果你的前端窗体是可以调整大小的,并且Panel大小也会随之变化的话,你会发现在每次Panel大小改变的时候,你嵌入的第三方窗体不会随着变大变小,如果你拉大窗体,你的Panel变大了,第三方窗体大小却没变,Panel里面就会空出来一块,非常难看,于是就有了下面的代码来实现父容器大小变化时,第三方窗体的自适应。

DispatcherTimer timer2 = new DispatcherTimer(); //WPF的定时器,Winform用Timer

//在窗体构造函数里把定时器绑定好,时间间隔设置为0.1秒
public MainWindow()
{
InitializeComponent();
timer2.Tick += new EventHandler(timer2_Tick); //绑定事件
timer2.Interval = TimeSpan.FromSeconds(0.1);
}

void timer2_Tick(object sender, EventArgs e)
{
//第三方窗体句柄不为空
if (SetWindow.intPtr!= IntPtr.Zero)
{
Thread t = new Thread(SetWindow.ResizeWindow);
t.Start(); //开线程刷新第三方窗体大小
Thread.Sleep(50); //略加延时
timer2.Stop(); //停止定时器
}
}

private void MainWindow1_SizeChanged(object sender, SizeChangedEventArgs e)
{
timer2.Start(); //在每次窗体大小改变时,开启定时器调整第三方窗体大小
}

上面是WPF的代码,Winform的话直接用工具箱里的Timer,把事件代码复制过去就行。

经过我的测试,使用定时器会减少闪烁的出现,并且也不会出现卡顿,下图为我将第三方的程序嵌入窗体的成果,用的是Winform。

将Steam嵌入Panel容器

可以看到第三方的窗体完美的嵌入到了Panel里面,大小也变成了相应的大小,拉大窗体,Panel大小变化第三方窗体也会跟着适应大小,效果非常NICE!

可能会遇到的问题:

1.有的窗体没有标题,那怎么知道那个窗体的名称?

答:在电脑屏幕最下方任务栏那里会有小图标,鼠标移上去会显示这个窗体的名字。

2.WPF没有Panel怎么办?

答:用WindowsFormsHost控件,Panel放在这个控件里。

3.除了Panel,能不能放其他容器里?

答:理论上可以,不过我并没有进行测试。

4.如何退出嵌入,使窗体回归自由状态?

答:将parent设置为空句柄即可,可以在SetWindow类里加上下面这个方法,在前端直接用SetWindow.ExitParent()调用就可以了。这里我用了两次ShowWindow是为了取消最大化(因为之前在Panel里面我们设置成了最大化,如果要恢复成原来的大小就要用ShowWindow)。

在恢复窗口自由之后,如果前面设置了Timer的话,由于Timer还在运行,在Panel大小改变之后会触发最大化,所以在取消嵌入后,还需要将Timer与事件解绑一下。

    public static void ExitParent()
    {
        ShowWindow(intPtr, 0);
        SetParent(IntPtr.Zero);
        ShowWindow(intPtr,9);
    }

我写了一版Winform的源码示例,大家可以去自行下载。

C#Winform实现窗体内嵌入第三方应用窗体源码,可自适应容器大小-C#文档类资源-CSDN下载
Winform实现窗体内嵌入第三方应用窗体,只需要知道窗体名称,即可将第三方应用的窗体嵌入到自己的程更多下载资源、学习资料请访问CSDN下载频道.
https://download.csdn.net/download/XX_YZDY/85762239
————————————————

                        版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/XX_YZDY/article/details/125442128

在Windows Forms (WinForms) 应用程序中嵌入 WPF 窗体需要一些额外步骤,因为WinForms和WPF是两个独立的框架。以下是一个简单的步骤说明: 1. **添加引用**:首先,在WinForms项目中右键单击“References”,然后选择“Add Reference”。在弹出的对话框里,找到并勾选“PresentationFramework”(如果它不在可用的列表里,可能需要安装.net Framework 4.5 或更高版本)。 2. **创建WPF窗体**:在WinForms项目中新建一个WPF窗体,例如命名为`WpfForm.xaml.cs`。这将生成一个WPF窗体文件和对应的后台代码。 3. **加载WPF窗体**:在WinForms上添加一个用户控件或者通过其他方式(如Panel或Window)来承载WPF窗体。例如,你可以使用`InteropServices`库中的`CreateObject`函数动态创建一个WPF窗口实例: ```csharp using System.Windows.Forms; using System.Runtime.InteropServices; private void ShowWpfFormButton_Click(object sender, EventArgs e) { // 调整WPF窗体的构造函数以接受适当的参数,如果有的话 string wpfWindowName = "YourWpfFormName"; object wpfInstance = Activator.CreateInstance(Type.GetTypeFromProgID($"WPFNamespace.{wpfWindowName}")); using (var window = new WindowInteropHelper(wpfInstance as Window)) { window.Show(); } } ``` 4. **处理窗体关闭事件**:记得在WPF窗体中处理Closing事件,以便在用户关闭时正确地关闭嵌入的WPF窗体。 注意:这种方法不是推荐的最佳实践,因为它涉及到COM interop,可能会导致性能和兼容性问题。通常更推荐的是使用Xceed或其他第三方控件(如Prism)来实现跨框架通信,或者考虑直接重构成统一的UI框架(如使用UWP或WPF)。如果你只是临时需要嵌套展示,可以考虑使用第三方库简化过程。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值