首页 > 解决方案 > 用鼠标拖动弹出窗口

问题描述

我有以下行为从这里用鼠标拖动弹出窗口

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

这可行,但是当我使用自定义放置将弹出窗口定位在窗口的右下角时,移动不是 1:1。

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

弹出窗口比鼠标移动更多,最终光标不再位于弹出窗口上方。

如何调整此代码,使弹出窗口与光标完全移动?

标签: c#wpf

解决方案


我相信这两者不能很好地协同工作,主要是因为有一些关于如何选择放置CustomPopupPlacementCallback以防万一的逻辑。

您观察到的行为是因为您在此处增加了偏移量:

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

这个偏移量是HorizontalOffsetVerticalOffset从拖动行为中设置的。但是您希望从该自定义回调返回的是相对位置,因此您不需要为其添加偏移量。例如,这将解决它:

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

但是,无论如何,在屏幕边缘附近的多显示器设置中,您都会遇到问题(与您现在遇到的问题不同)。拖拽时也会多次调用该回调,影响性能。

我建议改为只放置一次弹出窗口,打开。将 PlacementRelative设置为并PlacementTarget根据需要设置(可能您已经这样做了),然后只需计算打开时的偏移量。您可以在拖动行为本身中做到这一点(当然它不再只是拖动行为,但我不会在这里为干净的编码实践而烦恼):

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

推荐阅读