You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Delphi VCL窗体中嵌入C# WinForms Panel并实现自适应大小

解决方案

核心思路

直接调用Win32 API SetParent 仅修改了窗口的父子层级关系,但VCL和WinForms两个框架的布局管理逻辑都没有识别到这个跨框架的控件关联,因此不会自动触发大小适配,也不会将C# Panel纳入VCL的组件管理体系。下面给出可落地的实现方案:


1 Delphi侧改造

我们在Delphi侧增加一层容器控件承接C# Panel,同时将其纳入VCL组件管理体系:

1.1 窗体单元修改代码

unit FrmParent;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;

type
  TForm5 = class(TForm)
    pnlContainer: TPanel; // 设计期拖入,设置Align = alClient,BevelOuter = bvNone
    procedure FormCreate(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    FCSharpPanelHandle: HWND; // 存储C# Panel的窗口句柄
  public
    // 暴露给C#侧调用的方法,用于注册C# Panel句柄
    procedure RegisterCSharpPanel(Handle: HWND);
  end;

var
  Form5: TForm5;

implementation

{$R *.dfm}

procedure TForm5.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  // 销毁前通知C#侧释放资源,可根据实际交互逻辑调整
  if IsWindow(FCSharpPanelHandle) then
    SendMessage(FCSharpPanelHandle, WM_CLOSE, 0, 0);
end;

procedure TForm5.FormCreate(Sender: TObject);
begin
  Caption := IntToStr(Handle);
  FCSharpPanelHandle := 0;
end;

procedure TForm5.FormResize(Sender: TObject);
begin
  // 容器大小变化时,同步调整C# Panel大小
  if IsWindow(FCSharpPanelHandle) then
    SetWindowPos(FCSharpPanelHandle, 0, 0, 0, pnlContainer.ClientWidth, pnlContainer.ClientHeight, SWP_NOZORDER);
end;

procedure TForm5.RegisterCSharpPanel(Handle: HWND);
begin
  FCSharpPanelHandle := Handle;
  // 首次注册时直接设置初始大小
  SetWindowPos(FCSharpPanelHandle, 0, 0, 0, pnlContainer.ClientWidth, pnlContainer.ClientHeight, SWP_NOZORDER);
end;

1.2 可选优化:封装自定义VCL控件

如果需要完全让C# Panel成为VCL的一等公民,可以继承TWinControl封装自定义控件,在CreateParams中直接绑定C# Panel的句柄,这样无需手动处理Resize事件,VCL的布局逻辑会自动适配,同时控件会自动加入Components数组。


2 C#侧改造

调整WinForms侧的逻辑,适配跨框架父窗口:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Parenttest
{
    public partial class Form1 : Form
    {
        [DllImport("user32.dll")]
        public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
        [DllImport("user32.dll")]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
        private const uint SWP_NOZORDER = 0x0004;

        public Form1()
        {
            InitializeComponent();
            // 提前配置Panel属性
            panel1.Dock = DockStyle.Fill;
            // 隐藏主窗体,仅保留Panel
            this.ShowInTaskbar = false;
            this.FormBorderStyle = FormBorderStyle.None;
            this.WindowState = FormWindowState.Minimized;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // 这里替换为动态获取的Delphi容器Panel的Handle,不要写死
            IntPtr delphiContainerHandle = (IntPtr)919640; 
            SetParent(panel1.Handle, delphiContainerHandle);
            // 初始设置位置
            SetWindowPos(panel1.Handle, IntPtr.Zero, 0, 0, 0, 0, SWP_NOZORDER | 0x0001 /* SWP_NOSIZE */);
        }
    }
}

注意事项

  • 不要写死窗口句柄,建议通过进程间通信(如WM_COPYDATA消息、共享内存)将Delphi容器的句柄动态传给C#程序,避免每次运行句柄变化导致功能失效
  • 如果两个程序属于不同进程,需要处理跨进程窗口消息的权限问题,Windows Vista及以上系统默认开启UIPI限制,低权限进程无法向高权限进程的窗口发送消息,需配置manifest或者调整进程启动权限
  • 不需要复杂交互逻辑的前提下,上述代码已经可以实现自动适配大小的需求,不需要额外的变通方案

内容的提问来源于stack exchange,提问作者White

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 02:39:04