将窗口停靠在另一个窗口内

发布于 2024-11-03 20:02:54 字数 280 浏览 1 评论 0原文

我有一个 winform 应用程序(.NET 2.0 C#)。从这个应用程序中,我想启动另一个进程(另一个 winform 应用程序)并将其停靠到我的窗口(或至少使其看起来像是停靠的)。到目前为止,我只能找到有关停靠控件的信息,而找不到单独进程中的窗口的信息。我的第一个想法是获取窗口的句柄并使用非托管系统调用将窗口的高度/宽度和位置设置到我的停靠区域。但在开始之前,我想看看你们中是否有好人做过类似的事情。我可以访问我想要停靠的应用程序的源代码,但如果可以避免的话,我宁愿不进行任何更改。我对父应用程序有完全的编程控制。有什么建议吗?提前致谢!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我的鱼塘能养鲲 2024-11-10 20:02:54

我之前使用的解决方案是将应用程序窗口设置为要停靠它的控件的子级。

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);
}

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);
}
那些过往 2024-11-10 20:02:54

* 在答案中添加一些解决方案..**

此代码帮助我在 Windows 窗体中停靠一些可执行文件。例如记事本、Excel、word、Acrobat reader 等等...

但它不适用于某些应用程序。
有时当您启动某些应用程序的进程时...等待空闲时间...并尝试获取其 mainWindowHandle...直到主窗口句柄变为空...

所以我做了一个解决这个问题的技巧

如果你得到的主窗口句柄为空...然后搜索系统上所有正在运行的进程并找到你的进程...然后获取进程的主句柄并将设置面板作为其父级。

            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);
        }

* 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);
        }
帅哥哥的热头脑 2024-11-10 20:02:54

这比我希望的要笨重得多,但到目前为止还有效。我正在使用系统调用来强制子窗口位于反映停靠区域的位置。它还没有完美运行。我遇到了一些由 HWND_TOPMOST 引起的奇怪现象,我仍然需要添加逻辑以防止用户直接移动子窗口。

    //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);
    }

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);
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文