【问题标题】:Drag WPF Popup control拖动 WPF Popup 控件
【发布时间】:2010-09-18 07:59:16
【问题描述】:

WPF Popup 控件很不错,但在我看来有些局限。有没有办法在弹出窗口打开时“拖动”它(就像 windows 的 DragMove() 方法一样)?

这可以在没有大问题的情况下完成,还是我必须自己编写弹出类的替代品? 谢谢

【问题讨论】:

    标签: c# .net wpf xaml popup


    【解决方案1】:

    PopUp 没有 DragMove。只是一个小工作,你可以添加很多改进。

    <Popup x:Name="pop" IsOpen="True" Height="200" Placement="AbsolutePoint"  Width="200">
       <Rectangle Stretch="Fill" Fill="Red"/>            
    </Popup>
    

    在后面的代码中,添加这个 mousemove 事件

       pop.MouseMove += new MouseEventHandler(pop_MouseMove);
    
       void pop_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                pop.PlacementRectangle = new Rect(new Point(e.GetPosition(this).X,
                    e.GetPosition(this).Y),new Point(200,200));
    
            }
        }
    

    【讨论】:

    • 哎哟!不,请改用Thumb 及其DragDelta 事件(如果要管理状态,请选择DragStarted/DragCompleted)。如上例所示,它更高效,不会使内存碎片化,也不会冒丢失鼠标的风险。这就是正确完成拖动的方式:) 示例参见here
    • 我同意,我给出的解决方案只是一个 hack,正如你所说 DragDelta 比 MoveMove 更高效。
    • 但是,如果您不想将弹出窗口误用作拇指(我猜我在 RedGlyphs 评论中的意思),但想要移动更复杂的弹出窗口(例如视频叠加),该怎么办。在我的场景中,使用显式窗口不是选项。
    【解决方案2】:

    这是一个使用拇指的简单解决方案。

    • XAML 和代码隐藏中的子类弹出窗口
    • 添加宽度/高度设置为 0 的 Thumb(这也可以在 XAML 中完成)
    • 在 Popup 上监听 MouseDown 事件并在 Thumb 上引发相同的事件
    • 在 DragDelta 上移动弹出窗口

    XAML:

    <Popup x:Class="PopupTest.DraggablePopup" ...>
        <Canvas x:Name="ContentCanvas">
    
        </Canvas>
    </Popup>
    

    C#:

    public partial class DraggablePopup : Popup 
    {
        public DraggablePopup()
        {
            var thumb = new Thumb
            {
                Width = 0,
                Height = 0,
            };
            ContentCanvas.Children.Add(thumb);
    
            MouseDown += (sender, e) =>
            {
                thumb.RaiseEvent(e);
            };
    
            thumb.DragDelta += (sender, e) =>
            {
                HorizontalOffset += e.HorizontalChange;
                VerticalOffset += e.VerticalChange;
            };
        }
    }
    

    【讨论】:

    • 也许我遗漏了一些经验丰富的 WPF 开发人员会知道的简单的东西,但它如何可重用?当我在不同的 XAML 上下文中植入控件时(例如尝试在窗口中重用它),我指定的任何内容都会覆盖拇指所绑定的 Canvas 元素。
    • 我已经尝试过这个解决方案。效果很好!谢谢雅各布。我只面临一个问题:我在这个 Thumb 中有一个视图,我可以很好地拖动整个视图,但是如果该视图有一个滚动条,那么拖动滚动条会导致拖动拇指,从而拖动整个视图。有没有办法避免这种情况?
    【解决方案3】:

    实现此目的的另一种方法是将 Popup 的位置设置为 MousePoint。这使得弹出窗口最初出现在鼠标光标的位置。

    然后您可以使用 Thumb 或 MouseMove 事件来设置 Popup 的 Horizo​​ntalOffset 和 VerticalOffset。当用户拖动 Popup 时,这些属性会将其从其原始位置移开。

    记得将 Horizo​​ntalOffset 和 VerticalOffset 重置为零,以便下次使用弹出窗口!

    【讨论】:

      【解决方案4】:

      鼠标移动过快的问题,可以解决


      这取自msdn:

      新窗口包含 Popup 的子内容。

      Popup 控件将对其子内容的引用保持为逻辑子项。当新窗口创建时,Popup 的内容成为窗口的可视子窗口,并且仍然是 Popup 的逻辑子窗口。相反,Popup 仍然是其子内容的逻辑父级。


      换句话说,弹出窗口的子窗口显示在独立窗口中。

      因此,当尝试以下操作时:
      Popup.CaptureMouse() 正在捕获包装窗口而不是弹出窗口本身。而是使用 Popup.Child.CaptureMouse() 捕获实际的弹出窗口。

      所有其他事件都应该使用Popup.Child注册。

      比如Popup.Child.MouseMovePopup.Child.LostCapture等等

      这已经过测试并且运行良好

      【讨论】:

      • 是的,捕获鼠标输入是处理拖动的正确方法。所有这些 e.Leftbutton == Pressed 有点懒惰并会导致一些副作用。
      【解决方案5】:

      根据Jobi Joy 的回答,我找到了一个可重复使用的解决方案,允许您在现有控件/页面的 xaml 中添加为控件。这是不可能添加为 Xaml 的名称,因为它具有不同的范围。

          [ContentProperty("Child")]
          [DefaultEvent("Opened")]
          [DefaultProperty("Child")]
          [Localizability(LocalizationCategory.None)]
          public class DraggablePopup : Popup
          {
              public DraggablePopup()
              {
                  MouseDown += (sender, e) =>
                  {
                      Thumb.RaiseEvent(e);
                  };
      
                  Thumb.DragDelta += (sender, e) =>
                  {
                      HorizontalOffset += e.HorizontalChange;
                      VerticalOffset += e.VerticalChange;
                  };
              }
      
              /// <summary>
              /// The original child added via Xaml
              /// </summary>
              public UIElement TrueChild { get; private set; }
      
              public Thumb Thumb { get; private set; } = new Thumb
              {
                  Width = 0,
                  Height = 0,
              };
      
              protected override void OnInitialized(EventArgs e)
              {
                  base.OnInitialized(e);
      
                  TrueChild = Child;
      
                  var surrogateChild = new StackPanel();
      
                  RemoveLogicalChild(TrueChild);
      
                  surrogateChild.Children.Add(Thumb);
                  surrogateChild.Children.Add(TrueChild);
      
                  AddLogicalChild(surrogateChild);
                  Child = surrogateChild;
              }
          }
      

      【讨论】:

        【解决方案6】:

        与其他人对此的说法相反,我 100% 同意 Jobi Joy 的回答(老实说,这应该是公认的答案)。我看到一条评论指出答案中的解决方案会导致内存碎片。这是不可能的,因为创建新结构根本不会导致内存碎片;事实上,使用结构可以节省内存,因为它们是堆栈分配的。此外,我认为这实际上是重新定位弹出窗口的正确方法(毕竟,微软添加 PlacementRectangle 属性是有原因的),所以它不是一个 hack。然而,添加 Thumbs 并期望用户总是将 Popup 放置在画布上,这非常不自然,并不总是一个实用的解决方案。

        【讨论】:

          【解决方案7】:
          Private Point startPoint;
          
           private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
              {
          
                  startPoint = e.GetPosition(null);
              }
          private void Window_MouseMove(object sender, MouseEventArgs e)
              {
                  if (e.LeftButton == MouseButtonState.Pressed)
                  {
                      Point relative = e.GetPosition(null);
                      Point AbsolutePos = new Point(relative.X + this.Left, relative.Y + this.Top);
                      this.Top = AbsolutePos.Y - startPoint.Y;
                      this.Left = AbsolutePos.X - startPoint.X;
                  }
              }
          

          这适用于拖动我的窗口,但就像它被告知如果我快速移动鼠标,它会离开窗口并停止引发事件。更不用说拖动一点也不顺畅。有谁知道如何正确地做到这一点,漂亮而流畅的拖动,拖动过快时不会失去它???如果可能,请发布一个简单的示例,而不是让像我这样的初学者迷失在代码中的整个教程。谢谢!

          【讨论】:

          • 你可以捕捉鼠标来避免你提到的鼠标离开窗口的问题
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-11-17
          • 2011-01-02
          • 1970-01-01
          • 1970-01-01
          • 2018-12-03
          • 1970-01-01
          • 2010-12-22
          相关资源
          最近更新 更多