【问题标题】:WPF Window Drag/Move BoundaryWPF 窗口拖动/移动边界
【发布时间】:2010-03-22 00:29:38
【问题描述】:

只是好奇您是否知道为窗口设置拖动边界的任何方法?

如果有这些属性就好了:

Me.MinLeft = 10
Me.MinTop = 10
Me.MaxLeft = 150
Me.MaxTop = 150

顺便说一句,这些都是由属性组成的,如果有的话就太好了。

我知道我可能会设置一个计时器,每 10 秒触发一次,然后检查左侧和顶部,如果结束则将其移回。但是让窗口表现得像撞到墙上一样不能再进一步,比如移动到屏幕边缘或类似的东西会更优雅。

编辑:某处似乎有些混乱,我要说明的重点是在上面的段落中,拖动,而不是重新调整大小。

【问题讨论】:

    标签: wpf winforms


    【解决方案1】:

    这是创建此功能所需的“魔法”,您只需将 Window_SourceInitialized 方法设置为窗口的 SourceInitialized 事件,然后在大注释所在的位置插入逻辑。

    我从多个来源合并了这段代码,因此其中可能存在一些语法错误。

    internal enum WM
    {
       WINDOWPOSCHANGING = 0x0046,
    }
    
    [StructLayout(LayoutKind.Sequential)]
    internal struct WINDOWPOS
    {
       public IntPtr hwnd;
       public IntPtr hwndInsertAfter;
       public int x;
       public int y;
       public int cx;
       public int cy;
       public int flags;
    }
    
    private void Window_SourceInitialized(object sender, EventArgs ea)
    {
       HwndSource hwndSource = (HwndSource)HwndSource.FromVisual((Window)sender);
       hwndSource.AddHook(DragHook);
    }
    
    private static IntPtr DragHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handeled)
    {
       switch ((WM)msg)
       {
          case WM.WINDOWPOSCHANGING:
          {
              WINDOWPOS pos = (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS));
              if ((pos.flags & (int)SWP.NOMOVE) != 0)
              {
                  return IntPtr.Zero;
              }
    
              Window wnd = (Window)HwndSource.FromHwnd(hwnd).RootVisual;
              if (wnd == null)
              {
                 return IntPtr.Zero;
              }
    
              bool changedPos = false;
    
              // ***********************
              // Here you check the values inside the pos structure
              // if you want to override tehm just change the pos
              // structure and set changedPos to true
              // ***********************
    
              if (!changedPos)
              {
                 return IntPtr.Zero;
              }
    
              Marshal.StructureToPtr(pos, lParam, true);
              handeled = true;
           }
           break;
       }
    
       return IntPtr.Zero;
    }
    

    【讨论】:

    • 嗨。如果((pos.flags & (int)SWP.NOMOVE) != 0) 中的SWP 是什么?它应该被宣布为枚举吗? internal enum SWP { NOMOVE = 0x0002 }你能举个例子吗?
    【解决方案2】:

    我毫不怀疑 Nir ​​的答案会花一点时间来实现它,我可以用这段代码做我想做的更优雅的事情:

    Private Sub myWindow_LocationChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.LocationChanged
    
        Dim primaryBounds As System.Drawing.Rectangle = Windows.Forms.Screen.PrimaryScreen.Bounds
        Dim windowBounds As System.Drawing.Rectangle = New System.Drawing.Rectangle(CInt(Me.Left), CInt(Me.Top), CInt(Me.Width), CInt(Me.Height))
    
        If (windowBounds.Left < 0) Then
            windowBounds = New System.Drawing.Rectangle(0, windowBounds.Top, windowBounds.Width, windowBounds.Height)
        ElseIf (windowBounds.Right > primaryBounds.Right) Then
            windowBounds = New System.Drawing.Rectangle(primaryBounds.Right - windowBounds.Width, windowBounds.Top, windowBounds.Width, windowBounds.Height)
        End If
    
        If (windowBounds.Top < 0) Then
            windowBounds = New System.Drawing.Rectangle(windowBounds.Left, 0, windowBounds.Width, windowBounds.Height)
        ElseIf (windowBounds.Bottom > primaryBounds.Bottom) Then
            windowBounds = New System.Drawing.Rectangle(windowBounds.Left, primaryBounds.Bottom - windowBounds.Height, windowBounds.Width, windowBounds.Height)
        End If
    
        Me.Left = windowBounds.Left
        Me.Top = windowBounds.Top
    
    End Sub
    

    这使得被拖动的窗口停留在主屏幕(整个窗口)内,但您可以轻松地将边界更改为您需要的任何值。

    【讨论】:

    • 我以前试过这个,但是 Window_LocationChanged 事件发生在窗口已经在屏幕上移动之后,所以这会产生一种不和谐的视觉效果,当窗口开始越过时,它会闪烁或跳跃一些限制,因为它最初被“越界”绘制,然后重新绘制回您想要的位置。
    • 如何防止窗口闪烁?拖拽动作不流畅。
    【解决方案3】:

    为此目的,WPF 的 Window 有依赖属性。

    他们在这里:

    • Window.MaxWidth
    • Window.MaxHeight

    这些属性将限制窗口的大小,就像 WinForm 的窗体一样。

    【讨论】:

    • 我说的是在屏幕上拖动一个窗口,而不是重新调整它的大小。考虑在屏幕上设置一个区域,即使您的屏幕更大,窗口也只允许在其中移动。像拖动容器是我能想到的最好的。
    • 我坐在这里又读了一遍,想知道我是否问错了问题,你怎么能认为我说的是窗口大小而不是拖动,我什至没有提到“大小”这个词或在我的问题中“重新调整大小”...
    • 拖动边界?我认为它默认是可拖动的。您想要像 Windows 窗体中那样的拖动句柄吗?
    • 他想限制可以拖动窗口的区域。
    【解决方案4】:

    也许您可以处理PreviewMouseMove(事件或覆盖相应的受保护方法)并在鼠标移动导致窗口移动到您想要限制的区域之外时设置e.Handled = true

    这似乎是最合乎逻辑的、类似 WPF 的方式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-05
      • 1970-01-01
      相关资源
      最近更新 更多