将Unity3D应用程序嵌入WPF中*无需*占用整个窗口 [英] Embed Unity3D app inside WPF *without* having it take up the whole window

查看:653
本文介绍了将Unity3D应用程序嵌入WPF中*无需*占用整个窗口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试修改此问题中发布的代码: https://stackoverflow.com/a/44059700

I am trying to adapt the code posted in this question: https://stackoverflow.com/a/44059700

允许我将Unity3D应用程序嵌入WPF应用程序中.

to allow me to embed a Unity3D app inside a WPF app.

这是我稍作修改的版本:

This is my slightly edited version:

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的相关部分:

And here is the relevant part of the 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"/>

真的,唯一的区别是我试图在框架而不是WinForms面板中使用WPF页面(试图避免WinForms).嵌入式Unity应用程序可以很好地启动...除了占用整个窗口(即您再也看不到任何WPF控件)之外.

Really, the only difference is that I'm trying to use a WPF Page inside a Frame instead of a WinForms panel (trying to avoid WinForms). The embedded Unity app starts up fine...except that it takes up the whole window (i.e. you can't see any of the WPF controls anymore).

所以,这个问题: 如何使Unity应用程序仅停留在WPF页面(位于框架内)内?

So, the question: How do I get the Unity app to only stay inside the WPF page (which is inside a Frame)?

在此处输入图片描述

(此XY问题的Y在于我只是试图在WPF应用程序中创建3D图形显示的东西.)

(The Y of this XY problem would be that I'm just trying to create a 3D graphics display of something inside a WPF app.)

在此先感谢您的帮助.

推荐答案

在WPF中使用WindowsFormsHostHwndHost控件. hwnd在主机控件的Handle属性中.因此,您可以更改此行以将Unity放入主机控件中.

Use a WindowsFormsHost or HwndHost control in your WPF. The hwnd is in the host control's Handle property. So you can change this line to put Unity in just the host control.

process.StartInfo.Arguments = "-parentHWND " + hwndHost.Handle.ToInt32() + " " + Environment.CommandLine;

并删除获取浮动框架hwnd的代码

And remove the code that gets the hwnd for the floating frame

HwndSource source = (HwndSource)HwndSource.FromVisual(p);

这篇关于将Unity3D应用程序嵌入WPF中*无需*占用整个窗口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆