3

In my WPF, I have a grid that contains a WindowsFormsHost which will host an .exe. My .xaml code is:

<Window x:Class="PracticeWPF.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:PracticeWPF"
        mc:Ignorable="d"
        Title="MainWindow" Height="800" Width="1300">
    <Grid x:Name="grid">

        <Button x:Name="BtnRun" Content="Run" 
                HorizontalAlignment="Left" Margin="10,10,0,0"
                VerticalAlignment="Top" Click="BtnRun_Click"/>

        <WindowsFormsHost x:Name="formshost" Margin="0,39,0,0"
            Height="720" Width="1280"/>

    </Grid>
</Window>

And my cs:

namespace PracticeWPF
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

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

        private void BtnRun_Click(object sender, EventArgs e)
        {
            Process proc = Process.Start("notepad.exe"); 
            proc.WaitForInputIdle();

            while (proc.MainWindowHandle == IntPtr.Zero)
            {
                Thread.Sleep(100);
                proc.Refresh();
            }
            SetParent(proc.MainWindowHandle, formshost.Handle);
        }    
    }
}

Everything works when I'm using a WinForm but when I use the WindowsFormsHosting inside a WPF the .exe isn't visible. Any help would be greatly appreciated. Thank you.

2 Answers2

2

At this link Docking Window inside another Window I think your answer. I matched it with your code

xaml

<Window x:Class="PracticeWPF.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:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"  
        xmlns:local="clr-namespace:PracticeWPF"
        mc:Ignorable="d"
        Title="MainWindow" Height="800" Width="1300">
    <Grid x:Name="grid">

        <Button x:Name="BtnRun" Content="Run" 
                HorizontalAlignment="Left" Margin="10,10,0,0"
                VerticalAlignment="Top" Click="BtnRun_Click"/>

        <WindowsFormsHost x:Name="formshost" Margin="0,39,0,0"
            Height="720" Width="1280">
             <wf:Button Text="by button" Visible="False"></wf:Button>
        </WindowsFormsHost>
    </Grid>
</Window>

and mainwindow.cs

namespace PracticeWPF
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void BtnRun_Click(object sender, EventArgs e)
        {
            //Process proc = Process.Start("notepad.exe");
           //proc.WaitForInputIdle();

            //while (proc.MainWindowHandle == IntPtr.Zero)
            //{
            //    Thread.Sleep(100);
            //    proc.Refresh();
            //}
            this.Width = formshost.Width;
            this.Height = formshost.Height;
            dockIt();
        }


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

        private Process pDocked;
        private IntPtr hWndOriginalParent;
        private IntPtr hWndDocked;

        [DllImport("user32.dll", SetLastError = true)]
        public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);


        private void dockIt()
        {
            if (hWndDocked != IntPtr.Zero) //don't do anything if there's already a window docked.
                return;
            IntPtr hWndParent = IntPtr.Zero;

            pDocked = Process.Start(@"notepad");
            while (hWndDocked == IntPtr.Zero)
            {
                pDocked.WaitForInputIdle(1000); //wait for the window to be ready for input;
                pDocked.Refresh();              //update process info
                if (pDocked.HasExited)
                {
                    return; //abort if the process finished before we got a handle.
                }
                hWndDocked = pDocked.MainWindowHandle;  //cache the window handle
            }
            //Windows API call to change the parent of the target window.
            //It returns the hWnd of the window's parent prior to this call.
            hWndOriginalParent = SetParent(hWndDocked, formshost.Handle);

            //Wire up the event to keep the window sized to match the control
            formshost.SizeChanged += new SizeChangedEventHandler(Panel1_Resize);
            //Perform an initial call to set the size.
            Panel1_Resize(new Object(), new EventArgs());
        }

        private void undockIt()
        {
            //Restores the application to it's original parent.
            SetParent(hWndDocked, hWndOriginalParent);
        }

        private void Panel1_Resize(object sender, EventArgs e)
        {
            //Change the docked windows size to match its parent's size. 
            MoveWindow(hWndDocked, 0, 0, (int)formshost.Width, (int)formshost.Height, true);
        }
    }
}
Meysam Asadi
  • 6,438
  • 3
  • 7
  • 17
  • Thank you, this is what I was trying to do. Also thanks for helping with the resizing and filling window part, that was going to be tricky to work out. Just curious, why does the button need to be in the WindowsFormsHost, when I remove the button the application becomes invisible but I don't see a connection to the button in the cs. – Henry Smith Feb 09 '21 at 19:06
  • I was looking for the same point and found the solution in @ mm8. hWndOriginalParent = SetParent (hWndDocked, new System.Windows.Interop.WindowInteropHelper (this) .Handle); – Meysam Asadi Feb 09 '21 at 20:22
  • This is probably why WindowsFormsHost has no children and the compiler hides it and does not display it when an external application is Places. And all this is done while loading the window. – Meysam Asadi Feb 09 '21 at 20:38
  • Or when loaded, the form is rendered at zero height and can not be resized when the external program is placed, and when a new sample is created, the window is rendered with the external program. – Meysam Asadi Feb 10 '21 at 02:13
0

I am afraid a WindowsFormsHost cannot be used to host an entire Windows Forms application inside another application.

It is used to host an individual Windows Forms control in a WPF view.

If you want to get a handle to the WPF window, try with:

new System.Windows.Interop.WindowInteropHelper(this).Handle

You want to use this handle instead of formshost.Handle.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • Ahh, I just thought that the WindowsFormsHost was like adding a Windows Forms to a WPF. I tried that and it solved everything. Thanks for the help – Henry Smith Feb 09 '21 at 18:55
  • You realized that you accepted the other answer...? – mm8 Feb 09 '21 at 22:18