c# - 在 WPF 面板中托管外部应用程序

标签 c# wpf window exe hwnd

我正在尝试在程序主窗口内的面板中托管/运行 Exe 应用程序。主窗口有一个 3 列的网格 - 两列用垂直网格分割器均匀分割,以划分左列和右列。左列有一个选项卡控件,但右列是我想要将 exe 应用程序放置在其中的内容,并且本质上是根据右列内部的停靠栏调整应用程序的大小。

我以类似于 Hosting external app in WPF window 的方式构建了该程序。 。这个示例比我要用于 exe 和附加功能的示例要简单得多,但该示例应该解释我当前的问题。

我可以将 exe 启动到窗口中,但不能在 Dockpanel 或右列中的任何其他控件中启动。 exe 只是静态地保留在窗口内,而不是放置在列内。我可以进入并更改进程窗口的起始位置,以便它实际上在右列的顶部打开,并且似乎位于该列的停靠面板内,但同样它只是静态的。

我见过的所有示例都将进程放置在窗口内,而不是容器内。我尝试将 WindowsInteropHelper 设置到停靠面板,但它仍然只是将其发送到主窗口。

var helper = new WindowInteropHelper(GetWindow(this.ApplicationDock));

我还尝试了一些文章,包括下面的两篇文章,但仍然没有找到我正在寻找的解决方案。

How can I run another application within a panel of my C# program?

Docking Window inside another Window

我考虑过的一种可能性是,在窗口更改时更新进程窗口处理程序,将其放置在右列开始位置的正确坐标处。对我来说,这似乎应该是最后的手段,而不是处理这个问题的正确方法。

我的主要应用

<Window x:Class="ProgramInProgram.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:ProgramInProgram"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <TabControl Grid.Column="0">
        <TabItem Header="Blue Tab">
            <Grid Background="Blue"></Grid>
        </TabItem>
        <TabItem Header="Red Tab" >
            <Grid Background="Red"></Grid>
        </TabItem>
    </TabControl>
    <GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" />
    <DockPanel Grid.Column="2" x:Name="ApplicationDock">
    </DockPanel>
</Grid>
</Window>

背后的代码

public partial class MainWindow : Window
{

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

    [DllImport("user32.dll", SetLastError = true)]
    public static extern int GetWindowLong(IntPtr hWnd, int nIndex);

    [DllImport("user32.dll")]
    private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

    [DllImport("user32.dll")]
    private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int uFlags);

    private Process process;

    private const int SWP_ZOZORDER = 0x0004;
    private const int SWP_NOACTIVATE = 0x0010;
    private const int GWL_STYLE = (-16);
    private const int WS_CAPTION = 0x00C00000;
    private const int WS_VISIBLE = 0x10000000;
    private const int WS_THICKFRAME = 0x00040000;
    const string patran = "patran";

    public MainWindow()
    {
        InitializeComponent();

        Loaded += (s, e) => LaunchChildProcess();
    }

    private void LaunchChildProcess()
    {
        this.process = Process.Start("Notepad.exe");
        this.process.WaitForInputIdle();

        var helper = new WindowInteropHelper(GetWindow(this.ApplicationDock));

        SetParent(this.process.MainWindowHandle, helper.Handle);

        int style = GetWindowLong(this.process.MainWindowHandle, GWL_STYLE);
        style = style & ~WS_CAPTION & ~WS_THICKFRAME;
        SetWindowLong(this.process.MainWindowHandle, GWL_STYLE, style);
        ResizeEmbeddedApp();
    }

    private void ResizeEmbeddedApp()
    {
        if (this.process == null)
        {
            return;
        }

        UIElement container = VisualTreeHelper.GetParent(this.ApplicationDock) as UIElement;
        Point relativeLocation = this.ApplicationDock.TranslatePoint(new Point(0, 0), container);
        }

    protected override Size MeasureOverride(Size availableSize)
    {
        Size size = base.MeasureOverride(availableSize);
        ResizeEmbeddedApp();
        return size;
    }
}

最佳答案

我有同样的需求,因为我的客户想要一个运行 4 个 wpf 应用程序的仪表板。我相信这应该有所帮助,顺便说一句,如果有人知道构建 Windows 应用程序仪表板的更好方法,我很想听听。关键是windowsformhost的使用。

string exeName = @"C:\Repos\OnSpot17\OnTheSpot\bin\Debug\OnTheSpot.exe";
        var procInfo = new System.Diagnostics.ProcessStartInfo(exeName);
        procInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(exeName);
        procInfo.WindowStyle = ProcessWindowStyle.Minimized;
        // Start the process
        _childp = Process.Start(procInfo);
        System.Windows.Forms.Panel _pnlSched = new System.Windows.Forms.Panel();
        WindowsFormsHost windowsFormsHost1 = new WindowsFormsHost();

        windowsFormsHost1.Child = _pnlSched;

        test.Children.Add(windowsFormsHost1);

        // Wait for process to be created and enter idle condition
        //    _childp.WaitForInputIdle();
        // The main window handle may be unavailable for a while, just wait for it
        while (_childp.MainWindowHandle == IntPtr.Zero)
        {
            Thread.Yield();
        }

        // Get the main handle
        _appWin = _childp.MainWindowHandle;
  //      PR.WaitForInputIdle(); // true if the associated process has reached an idle state.
        SetParent(_appWin, _pnlSched.Handle); // loading exe to the wpf window.

关于c# - 在 WPF 面板中托管外部应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40477961/

相关文章:

c# - RadGrid 双击

c# - 如何在 EntityKey 上编写通用主键查找器?

c# - OxyPlot 在 PlotView/PlotModel 中被点击位置

javascript - 在新窗口中显示图像

c# - Xamarin Forms iOS 应用程序检测到 "Return"已按下

c# - CheckedListBox 选择所有项目 - Windows 窗体 C#

c# - 以编程方式启动 Windows 10 表情符号热键

c# - 如何计算WPF中从一个区域/矩形到另一个区域/矩形的变换矩阵?

python - pandas:计算 Blackman 窗口的最大值:缺少函数 pandas.core.window.Window.[max,apply]

window - PyGTK 透明窗口