我正在尝试在程序主窗口内的面板中托管/运行 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/