在某个边界WPF内创建一个可拖动的窗口

Dan*_*Dan 3 wpf wpf-controls

我有一个wpf子窗口,我允许使用DragMove()方法进行拖动.但是,我需要允许窗口仅在其父窗口控件的范围内拖动.

谁能建议一种方法来实现这一目标?谢谢!

Con*_*rix 5

有两种方法可以做到这一点.

使用LocationEnded

如果处理此事件,则可以将"顶部"或"左"更改为属于所有者窗口的范围.例如

    private void Window_LocationChanged(object sender, EventArgs e)
    {

        if (this.Left < this.Owner.Left)
            this.Left = this.Owner.Left;

        //... also right top and bottom
        //
    }
Run Code Online (Sandbox Code Playgroud)

这很容易编写,但它违反了最不惊讶原则,因为当用户放开鼠标按钮时它不会将窗口推回到位置的拖动窗口.

使用AddHook

正如Pieter在回答类似问题时指出的那样,您可以互操作Windows消息并阻止拖动窗口.这具有限制实际拖动窗口的良好效果.

这是我为AddHook方法整理的一些示例代码

从加载的窗口开始添加钩子

  //In Window_Loaded the handle is there (earlier its null) so this is good time to add the handler 

   private void Window_Loaded(object sender, RoutedEventArgs e)
    {

        WindowInteropHelper helper = new WindowInteropHelper(this);
        HwndSource.FromHwnd(helper.Handle).AddHook(HwndSourceHookHandler);
    } 
Run Code Online (Sandbox Code Playgroud)

在您的处理程序中,您只想查找移动或移动消息.然后你将读取lParam矩形并查看它是否超出范围.如果是,你需要更改lParam矩形的值并将其重新编号.请注意,为了简洁起见,我只做了左边.你仍然需要写出正确的,顶部和底部的案例.

 private IntPtr HwndSourceHookHandler(IntPtr hwnd, int msg, IntPtr wParam,
    IntPtr lParam, ref bool handled)
        {


const int WM_MOVING = 0x0216;
        const int WM_MOVE = 0x0003;


        switch (msg)
        {
            case WM_MOVING:
                {


                   //read the lparm ino a struct

                    MoveRectangle rectangle = (MoveRectangle)Marshal.PtrToStructure(
                      lParam, typeof(MoveRectangle));


                     //

                    if (rectangle.Left < this.Owner.Left)
                    {
                        rectangle.Left = (int)this.Owner.Left;
                        rectangle.Right = rectangle.Left + (int)this.Width;
                    }



                    Marshal.StructureToPtr(rectangle, lParam, true);

                    break;
                }
            case WM_MOVE:
                {
                   //Do the same thing as WM_MOVING You should probably enacapsulate that stuff so don'tn just copy and paste

                    break;
                }


        }

        return IntPtr.Zero;

    }
Run Code Online (Sandbox Code Playgroud)

lParam的结构

  [StructLayout(LayoutKind.Sequential)]
    //Struct for Marshalling the lParam
    public struct MoveRectangle
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;

    }
Run Code Online (Sandbox Code Playgroud)

最后要注意的是,如果允许子窗口大于父窗口,则需要弄清楚该怎么做.