我正在尝试修改这个问题中发布的代码:https://stackoverflow.com/a/44059700
允许我将Unity3D应用程序嵌入WPF应用程序。
这是我稍微编辑过的版本:
namespace WPFWithUnity
{
public partial class Page1 : Page
{
[DllImport("User32.dll")]
static extern bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);
internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);
[DllImport("user32.dll")]
internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);
[DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
private Process process;
private IntPtr unityHWND = IntPtr.Zero;
private const int WM_ACTIVATE = 0x0006;
private readonly IntPtr WA_ACTIVE = new IntPtr(1);
private readonly IntPtr WA_INACTIVE = new IntPtr(0);
Frame p = MainWindow.Instance.floatingFrame;
bool initialized = false;
public Page1()
{
InitializeComponent();
MainWindow.Instance.MainWindowClosing += Application_Exit;
System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += attemptInit;
dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
dispatcherTimer.Start();
}
void attemptInit(object sender, EventArgs e) {
if (initialized)
return;
HwndSource source = (HwndSource)HwndSource.FromVisual(p);
Console.WriteLine("attempting to get handle...");
if (source == null) {
Console.WriteLine("Failed to get handle source");
return;
}
IntPtr hWnd = source.Handle;
try
{
process = new Process();
process.StartInfo.FileName = "Child.exe";
process.StartInfo.Arguments = "-parentHWND " + hWnd.ToInt32() + " " + Environment.CommandLine;
process.StartInfo.UseShellExecute = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
process.WaitForInputIdle();
// Doesn't work for some reason ?!
//unityHWND = process.MainWindowHandle;
EnumChildWindows(hWnd, WindowEnum, IntPtr.Zero);
//unityHWNDLabel.Text = "Unity HWND: 0x" + unityHWND.ToString("X8");
Console.WriteLine("Unity HWND: 0x" + unityHWND.ToString("X8"));
panel1_Resize(this, EventArgs.Empty);
initialized = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + ".\nCheck if Container.exe is placed next to UnityGame.exe.");
}
}
private void ActivateUnityWindow()
{
SendMessage(unityHWND, WM_ACTIVATE, WA_ACTIVE, IntPtr.Zero);
}
private void DeactivateUnityWindow()
{
SendMessage(unityHWND, WM_ACTIVATE, WA_INACTIVE, IntPtr.Zero);
}
private int WindowEnum(IntPtr hwnd, IntPtr lparam)
{
unityHWND = hwnd;
ActivateUnityWindow();
return 0;
}
private void panel1_Resize(object sender, EventArgs e)
{
MoveWindow(unityHWND, 0, 0, (int)p.Width, (int)p.Height, true);
Console.WriteLine("RESIZED UNITY WINDOW TO: " + (int)p.Width + "x" + (int)p.Height);
ActivateUnityWindow();
}
// Close Unity application
private void Application_Exit(object sender, EventArgs e)
{
try
{
process.CloseMainWindow();
Thread.Sleep(1000);
while (!process.HasExited)
process.Kill();
}
catch (Exception)
{
}
}
private void Form1_Activated(object sender, EventArgs e)
{
ActivateUnityWindow();
}
private void Form1_Deactivate(object sender, EventArgs e)
{
DeactivateUnityWindow();
}
}
}
下面是XAML的相关部分:
<Frame Name="floatingFrame" Grid.Row="15" Grid.RowSpan="5" Grid.Column="0" Grid.ColumnSpan="2" Width="640" Height="480" Margin="100,0,0,0" Panel.ZIndex="100" Source="Page1.xaml"/>
真的,唯一的区别是,我尝试使用框架内的WPF页面,而不是WinForms面板(试图避免WinForms)。嵌入式Unity应用程序启动良好...除了它占据了整个窗口(即,您再也看不到任何WPF控件)。
**所以,问题是:**如何让Unity应用程序只停留在WPF页面内(位于Frame内)?
enter image description here
(The这个XY问题的Y是,我只是试图在WPF应用程序内部创建一个3D图形显示。)
提前感谢您的帮助。
2条答案
按热度按时间4dc9hkyq1#
在WPF中使用
WindowsFormsHost
或HwndHost
控件。hwnd位于宿主控件的Handle
属性中。因此,您可以更改此行以将Unity仅放在宿主控件中。并删除获取浮动帧的hwnd的代码
z2acfund2#
上述解决方案的问题是将焦点放在Unity-exe上似乎是不可能的。所以,是的,我能够在某个WPF应用程序的某个用户控件的某个单元格上加载exe,但不能单击Unity-frame中的任何内容。
我花了一整天寻找解决方案,现在可以来一个解决方案,这是在我看来更清洁,也解决了问题的重点。
我将分步骤描述您可以做的事情:
Form1.cs包含的代码与this question中的完全相同。
1.不要忘记在“Form1.cs”中查看,有可能您要更新的exe不是“Child.exe”,因此如果它是另一个,只需编辑代码中的字符串。
process.StartInfo.FileName = "[INSERT_FILE_NAME_OF_YOUR_EXE].exe";
还检查“SelectablePanel.cs”,其中Selectable设置为true是必不可少的。此SelectablePanel用于Form1.Designer.cs
。SplitContainer的左面板1是SelectablePanel而不是Panel!
还请注意,在Winforms-control的构造函数中,我需要设置
Toplevel = false;
。Unity的示例中没有提到这一点,但在将其嵌入WPF应用程序时需要避免异常。1.转到您的WPF应用程序并创建一个将包含Winforms-Control的用户控件。创建一个类似于this link中所做的控件。在此示例中,您有一个名为
Grid_To_Embed_Winforms_Control_In
的网格和一小段代码隐藏,如基础代码。1.不要忘记在顶部添加
using System.Windows.Forms.Integration;
。请注意,我添加boolean Already Loaded
是为了确保当视口发生变化(您希望查看WPF应用程序的另一个页面)时,进程不会再次启动。在我的完整解决方案中,我使用了Microsoft Dependency Injection,并且此控件位于作为单例添加的视图模型中。这样,我只启动进程一次。就是这样,这对我很有效.
什么是优于其他解决方案,我看到,而谷歌:
PS:如果你想让它看起来更漂亮,更“嵌入”,你可以用Winform控件做以下事情: