【问题标题】:Docking Window inside another Window将窗口停靠在另一个窗口中
【发布时间】:2011-08-15 17:21:45
【问题描述】:

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

【问题讨论】:

标签: c# winforms .net-2.0 dllimport docking


【解决方案1】:

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

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

【讨论】:

  • 谢谢! ''while (hWndDocked == IntPtr.Zero){}'' 循环也解决了我的一些问题。
  • 这很好,但它不适用于某些应用程序...例如资源管理器和 chrome。
  • 那么我该如何让Panel1适应Form呢?
【解决方案2】:

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

此代码帮助我将一些可执行文件停靠在 Windows 窗体中。比如记事本、Excel、word、Acrobat 阅读器等等……

但它不适用于某些应用程序。 有时当您启动某个应用程序的进程时......等待空闲时间......并尝试获取它的 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);
        }

【讨论】:

    【解决方案3】:

    这比我希望的要笨重得多,但到目前为止还有效。我正在使用系统调用来强制子窗口位于反映停靠区域的位置。它还不能完美地工作。我得到了由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);
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多