简体   繁体   English

拖动 WPF 弹出控件

[英]Drag WPF Popup control

the WPF Popup control is nice, but somewhat limited in my opinion. WPF Popup 控件很好,但在我看来有些有限。 is there a way to "drag" a popup around when it is opened (like with the DragMove() method of windows)?有没有办法在打开时“拖动”弹出窗口(例如使用窗口的 DragMove() 方法)?

can this be done without big problems or do i have to write a substitute for the popup class myself?这可以在没有大问题的情况下完成还是我必须自己编写弹出类的替代品? thanks谢谢

There is no DragMove for PopUp. PopUp 没有 DragMove。 Just a small work around, there is lot of improvements you can add to this.只是一个小小的工作,你可以添加很多改进。

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

In the code behind , add this mousemove event在后面的代码中,添加这个 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));

        }
    }

Here's a simple solution using a Thumb.这是一个使用 Thumb 的简单解决方案。

  • Subclass Popup in XAML and codebehind XAML 和代码隐藏中的子类弹出窗口
  • Add a Thumb with width/height set to 0 (this could also be done in XAML)添加宽度/高度设置为 0 的 Thumb(这也可以在 XAML 中完成)
  • Listen for MouseDown events on the Popup and raise the same event on the Thumb在 Popup 上侦听 MouseDown 事件并在 Thumb 上引发相同的事件
  • Move popup on DragDelta在 DragDelta 上移动弹出窗口

XAML: XAML:

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

    </Canvas>
</Popup>

C#: 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;
        };
    }
}

Another way of achieving this is to set your Popup's placement to MousePoint.实现此目的的另一种方法是将 Popup 的位置设置为 MousePoint。 This makes the popup initially appear at the position of the mouse cursor.这使得弹出窗口最初出现在鼠标光标的位置。

Then you can either use a Thumb or MouseMove event to set the Popup's HorizontalOffset & VerticalOffset.然后您可以使用 Thumb 或 MouseMove 事件来设置 Popup 的 Horizo​​ntalOffset 和 VerticalOffset。 These properties shift the Popup away from its original position as the user drags it.当用户拖动它时,这些属性将 Popup 从其原始位置移开。

Remember to reset HorizontalOffset and VerticalOffset back to zero for the next use of the popup!请记住将 Horizo​​ntalOffset 和 VerticalOffset 重置为零,以便下次使用弹出窗口!

The issue with loosing the mouse when moving too fast, could be resolved移动太快时失去鼠标的问题,可以解决


This is taken from msdn:这是从 msdn 中获取的:

The new window contains the Child content of Popup.新窗口包含 Popup 的 Child 内容。

The Popup control maintains a reference to its Child content as a logical child. Popup 控件维护对其子内容的引用作为逻辑子项。 When the new window is created, the content of Popup becomes a visual child of the window and remains the logical child of Popup.创建新窗口时,Popup 的内容成为窗口的可视子窗口,并保持为 Popup 的逻辑子窗口。 Conversely, Popup remains the logical parent of its Child content.相反,Popup 仍然是其子内容的逻辑父级。


In the other words, the child of the popup is displayed in standalone window.换句话说,弹出窗口的子窗口显示在独立窗口中。

So when trying to the following:因此,当尝试以下操作时:
Popup.CaptureMouse() is capturing the wrapper window and not the popup itself. Popup.CaptureMouse()正在捕获包装窗口而不是弹出窗口本身。 Instead using Popup.Child.CaptureMouse() captures the actual popup.而是使用Popup.Child.CaptureMouse()捕获实际的弹出窗口。

And all other events should be registered using Popup.Child .所有其他事件都应该使用Popup.Child注册。

Like Popup.Child.MouseMove , Popup.Child.LostCapture and so onPopup.Child.MouseMovePopup.Child.LostCapture

This has been tested and works perfectly fine这已经过测试并且运行良好

Building off of Jobi Joy 's answer, I found a re-useable solution that allows you to add as a control within xaml of an existing control/page.基于Jobi Joy的回答,我找到了一个可重复使用的解决方案,它允许您在现有控件/页面的 xaml 中作为控件添加。 Which was not possible adding as Xaml with a Name since it has a different scope.这是不可能添加为 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;
        }
    }

Contrary to what others have stated about this, I agree 100% with Jobi Joy's answer (which should honestly be the accepted answer).与其他人对此的说法相反,我 100% 同意 Jobi Joy 的回答(老实说,这应该是公认的答案)。 I saw a comment stating that the solution in the answer would cause memory fragmentation.我看到一条评论说答案中的解决方案会导致内存碎片。 This is not possible as creating new structs cannot cause memory fragmentation at all;这是不可能的,因为创建新结构根本不会导致内存碎片; in fact, using structs saves memory because they are stack-allocated.事实上,使用结构体可以节省内存,因为它们是堆栈分配的。 Furthermore, I think that this is actually the correct way to reposition a popup (after all, Microsoft added the PlacementRectangle property for a reason), so it is not a hack.此外,我认为这实际上是重新定位弹出窗口的正确方法(毕竟,微软出于某种原因添加了 PlacementRectangle 属性),因此它不是黑客。 Appending Thumbs and expecting a user to always place a Popup onto a canvas, however, is incredibly hacky and is not always a practical solution.然而,附加 Thumbs 并期望用户始终将 Popup 放置在画布上是非常笨拙的,并且并不总是一个实用的解决方案。

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

This works for dragging my window, but like it was told if i move the mouse to fast, it would get out of window and stop raising the event.这适用于拖动我的窗口,但就像有人告诉我如果我快速移动鼠标,它会离开窗口并停止引发事件。 Without mentioning the dragging is not smooth at all.更不用说拖动根本不顺畅。 Does anyone knows how to do it properly, nice and smooth dragging, without loosing it when dragged too fast???有谁知道如何正确地进行拖动,漂亮而平滑的拖动,拖动太快时不会丢失它??? Post a simple example if possible, other than a whole tutorial that would get beginners like me lost in code.如果可能的话,发布一个简单的例子,而不是一个会让像我这样的初学者迷失在代码中的整个教程。 Thanks!谢谢!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM