在WPF面板中托管外部应用

时间:2016-11-08 02:23:57

标签: c# wpf window exe hwnd

我正在尝试在程序主窗口内的面板中托管/运行Exe应用程序。主窗口有一个有3列的网格 - 两列用一个垂直网格分割器均匀分割,以分割左右列。左列有一个制表符控件,但是右列是我想要将exe应用程序放在其中的,并且基本上让应用程序根据右列内的dock调整大小。

我以与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;
    }
}

1 个答案:

答案 0 :(得分:0)

我有同样的需求,因为我的客户想要一个运行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.