Home > Enterprise >  Dragging a Popup with the mouse
Dragging a Popup with the mouse

Time:10-12

I've got the following behavior to drag a popup with the mouse from here

A draggable popup control in wpf

public class MouseDragPopupBehavior : Behavior<Popup>
{
    private bool mouseDown;
    private Point oldMousePosition;

    protected override void OnAttached()
    {
        AssociatedObject.MouseLeftButtonDown  = (s, e) =>
        {
            mouseDown = true;
            oldMousePosition = AssociatedObject.PointToScreen(e.GetPosition(AssociatedObject));
            AssociatedObject.Child.CaptureMouse();
        };
        AssociatedObject.MouseMove  = (s, e) =>
        {
            if (!mouseDown) return;
            var newMousePosition = AssociatedObject.PointToScreen(e.GetPosition(AssociatedObject));
            var offset = newMousePosition - oldMousePosition;
            oldMousePosition = newMousePosition;
            AssociatedObject.HorizontalOffset  = offset.X;
            AssociatedObject.VerticalOffset  = offset.Y;
        };
        AssociatedObject.MouseLeftButtonUp  = (s, e) =>
        {
            mouseDown = false;
            AssociatedObject.Child.ReleaseMouseCapture();
        };
    }
}

this works, but the movement is not 1:1 when i use Custom Placement to position the popup in the bottom right of my window.

public class MyPopup : Popup
{
...
    CustomPopupPlacementCallback  = (Size popupSize, Size targetSize, Point offset) =>
    {
        offset.X  = targetSize.Width - popupSize.Width - 50;
        offset.Y  = targetSize.Height - popupSize.Height - 100;
        return new[] { new CustomPopupPlacement(offset, PopupPrimaryAxis.Horizontal) };
    };
}

the popup moves more than the mouse, and eventually the cursor is no longer overtop of the popup.

how can i adjust this code so the popup is moved exactly with the cursor?

CodePudding user response:

I believe those two do not work well together, mostly because there is some non-trivial logic about how placement is chosen in CustomPopupPlacementCallback case.

The behaviour you observe is because you increment offset here:

offset.X  = targetSize.Width - popupSize.Width - 50;
offset.Y  = targetSize.Height - popupSize.Height - 100;

And that offset is HorizontalOffset and VerticalOffset you set from drag behavior. But what you expect to return from that custom callback is relative position, so you don't need to add offset to it. For example that will fix it:

var x = targetSize.Width - popupSize.Width - 50;
var y = targetSize.Height - popupSize.Height - 100;
return new[] { new CustomPopupPlacement(new Point(x, y), PopupPrimaryAxis.Vertical) };

However, you'll experience issues in multi-monitor setups anyway, near the edge of the screen (different issues from what you have now). This callback is also called many times when you perform a drag, affecting perfomance.

I'd suggest to instead just place popup once, on open. Set Placement to Relative and set PlacementTarget as desired (probably you already do), then just calculate the offset on open. You can do that in the drag behaviour itself (then of course it's not just drag behaviour anymore, but I'll won't bother with clean coding practices here):

AssociatedObject.Opened  = (o, e) => {
    var popupRoot = (FrameworkElement)AssociatedObject.Child;
    var target = (FrameworkElement)AssociatedObject.PlacementTarget; // you can use "constraint" from other question here
    AssociatedObject.HorizontalOffset = target.ActualWidth - popupRoot.ActualWidth - 50;
    AssociatedObject.VerticalOffset = target.ActualHeight - popupRoot.ActualHeight - 100;
};
  • Related