Docking Window inside another Window

2019-01-07 07:41发布

I have a winform application (.NET 2.0 C#). From this application, I want to start another process (another winform application) and dock it to my window (or at least make it look like it is docked). So far, I can only find information about docking controls, not windows in separate processes. My first thought is to get the handle of the window and use unmanaged system calls to set the height/width and position of the window to my docking area. But before I got started, I wanted to check to see if any of you good people have done something similar. I have access to the source code of the application I want docked but would rather not make any changes if I can avoid it. I have complete programming control over what will be the parent application. Any advice? Thanks in advance!

3条回答
疯言疯语
2楼-- · 2019-01-07 07:50

This is a lot clunkier than I hoped for, but working so far. I am using system calls to force the child window in the location that reflects the docking area. It is not working perfectly yet. I get a few oddities caused by the HWND_TOPMOST and I still need to add logic preventing the user from moving the child window directly.

    //This is my docking window
    private System.Diagnostics.Process notepad;
    private void windowDockTest()
    {
        /*
         * Docking notepad to panel2 of the splitcontainer
         */

        //if panel2 moves or is resized, call the docking function
        spcScript.Panel2.Move += new EventHandler(Panel2_Resize);
        spcScript.Panel2.SizeChanged += new EventHandler(Panel2_Resize);

        //Call the docking function if main form is moved
        this.LocationChanged += new EventHandler(Panel2_Resize);

        //Start the notepad process
        notepad = new System.Diagnostics.Process();
        notepad.StartInfo.FileName = "notepad";
        notepad.Start();

        //Wait a second for notpad to fully load
        notepad.WaitForInputIdle(1000);

        //Dock it
        Panel2_Resize(new Object(), new EventArgs());
    }

    void Panel2_Resize(object sender, EventArgs e)
    {
        //Get the screen location of panel2
        Rectangle r = spcScript.Panel2.RectangleToScreen(spcScript.Panel2.ClientRectangle);

        //Dock it
        redock(notepad.MainWindowHandle, r.X, r.Y, r.Width, r.Height);
    }

    [DllImport("user32.dll")]
    public static extern IntPtr SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
    public static void redock(IntPtr handle, int x, int y, int width, int height)
    {
        IntPtr HWND_TOPMOST = new IntPtr(-1);
        const short SWP_NOACTIVATE = 0x0010;

        SetWindowPos(handle,HWND_TOPMOST, x, y, width, height,SWP_NOACTIVATE);
    }
查看更多
时光不老,我们不散
3楼-- · 2019-01-07 07:55

* Adding some solution in Answer..**

This code has helped me to dock some executable in windows form. like NotePad, Excel, word, Acrobat reader n many more...

But it wont work for some applications. As sometimes when you start process of some application.... wait for idle time... and the try to get its mainWindowHandle.... till the time the main window handle becomes null.....

so I have done one trick to solve this

If you get main window handle as null... then search all the runnning processes on sytem and find you process ... then get the main hadle of the process and the set panel as its parent.

            ProcessStartInfo info = new ProcessStartInfo();
            info.FileName = "xxxxxxxxxxxx.exe";
            info.Arguments = "yyyyyyyyyy";
            info.UseShellExecute = true;
            info.CreateNoWindow = true;
            info.WindowStyle = ProcessWindowStyle.Maximized;
            info.RedirectStandardInput = false;
            info.RedirectStandardOutput = false;
            info.RedirectStandardError = false;

            System.Diagnostics.Process p = System.Diagnostics.Process.Start(info); 

            p.WaitForInputIdle();
            Thread.Sleep(3000);

            Process[] p1 ;
        if(p.MainWindowHandle == null)
        {
            List<String> arrString = new List<String>();
            foreach (Process p1 in Process.GetProcesses())
            {
                // Console.WriteLine(p1.MainWindowHandle);
                arrString.Add(Convert.ToString(p1.ProcessName));
            }
            p1 = Process.GetProcessesByName("xxxxxxxxxxxx");
            //p.WaitForInputIdle();
            Thread.Sleep(5000);
           SetParent(p1[0].MainWindowHandle, this.panel2.Handle);
        }
         else
        {
           SetParent(p.MainWindowHandle, this.panel2.Handle);
        }
查看更多
ゆ 、 Hurt°
4楼-- · 2019-01-07 08:05

The solution I have used before is to set the application window as a child of the control you want to dock it in.

using System.Diagnostics;
using System.Runtime.InteropServices;

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

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

[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;
    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, Panel1.Handle);

    //Wire up the event to keep the window sized to match the control
    Panel1.SizeChanged += new EventHandler(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, Panel1.Width, Panel1.Height, true);
}
查看更多
登录 后发表回答