c# - 我的可拖动弹出窗口也会拖动拥有它们的视觉元素。我怎样才能解决这个问题?

标签 c# wpf

更新:我发现,如果我总是从 MapCanvas 中的 OnMouseLeftButtonDown 返回,我就能按照我想要的方式使用弹出窗口。这向我表明 MapCanvas 的行为正在阻止罪魁祸首。我的问题可能很快就会有答案。

我担心导致这个问题的最终问题还没有解决,所以我会尽可能多地记录我的工作。对于这篇很长的帖子,我深表歉意,但我想尽可能清楚地分享我所拥有的。下面是我面临的问题的摘要:

我有一个由 map 线包含的弹出窗口,它本身包含在 map 中。当用户左键单击并拖动时,它会拖动 map 。我写了一个行为也可以单击并拖动弹出窗口。当我左键单击弹出窗口并尝试拖动它时,我改为拖动 map - 弹出窗口保持静止。当我松开鼠标左键时,弹出窗口就会“粘”在我的光标上,它会拖到我移动鼠标的任何地方。

好了,现在开始详细介绍:

我编写了一个在 WPF 应用程序中使用的自定义映射控件。我无法分享整个定义,但一些重要的部分如下所示:

public class MapCanvas : Canvas
{
    public override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
    {
        base.OnMouseLeftButtonDown(e);
        if (e.ClickCount >= 2)
        {
            //We zoom in a level
        }
        else
        {
            this.Focus(); //We get the keyboard
            if (this.CaptureMouse())
            {
                mouseCaptured = true;
                previousMouse = e.GetPosition(null);
            }
        }
    }

    public override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
    {
        base.OnMouseLeftButtonUp(e);
        this.ReleaseMouseCapture();
        mouseCaptured = false;
    }

    public override void OnMouseMove(MouseButtonEventArgs e)
    {
        base.OnMouseMove(e);
        if (mouseCaptured)
        {
            //Translate the map tiles based on the current position of the mouse
        }
    }
}

我共享覆盖,因为它们是我处理 map 交互(例如拖动 map 、双击缩放等)的方式。我还认为它在导致问题的原因中发挥了作用。

我还编写了一个名为 MapLine 的类型,它绑定(bind)到一组地理空间坐标,将它们转换为屏幕点,并将它们显示在 map 上。这些行被添加到我的 MapCanvas 中的 VisualCollection。我已经修剪了它们的定义并将其布置在下面:

public abstract class MapLine : FrameworkElement
{
    private VisualCollection popupChildren;

    private VisualCollection regularChildren;
    protected override int VisualChildrenCount
    {
        get
        {
            return regularChildren.Count;
        }
    }

    protected override Visual GetVisualChild(int index)
    {
        if (index < 0 || index >= VisualChildrenCount)
            throw new ArgumentOutOfRangeException("index");
        else
        {
            return regularChildren[index];
        }
    }

    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
    {
        HitTestResult result = VisualTreeHelper.HitTest(this, Mouse.GetPosition(this));
        if (result != null && result.VisualHit is DrawingVisual)
        {
            //If we find the Visual in our collection of regular children, we launch a new popup
            if(ThisIsRegularChild((DrawingVisual)result.VisualHit))
            {
                Popup pointPopup = GeneratePopup();
                this.popupChildren.Add(pointPopup);
                pointPopup.IsOpen = true;
            }
        }

        base.OnMouseLeftButtonDown(e);
    }

    private static Popup GeneratePopup()
    {
        Popup popup = new Popup();
        Stream popupContentStream = currentAssembly.GetManifestResourceStream("ResourcePath.xaml");
        popup.Child = (UIElement)XamlReader.Load(popupContentStream);
        popup.StaysOpen = true;
        popup.Placement = PlacementMode.Mouse;
        popup.AllowsTransparency = true;

        Interaction.GetBehaviors(popup).Add(new PopupDragManager());

        return popup;
    }
}

好吧,总结一下我分享的有关 MapLine 的内容:它还覆盖 OnMouseLeftButtonDown,并检查用户是否单击了此 MapLine 拥有的可视元素;如果我们这样做,我们会启动一个弹出窗口。该 Popup 附加了一个 System.Windows.Interactivity.Behavior - 这似乎是满足我使 popop 可拖动的需求的最简单方法。这是行为的定义:

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

        protected override void OnAttached()
        {
            AssociatedObject.MouseLeftButtonUp += MouseLeftButtonUp;
            AssociatedObject.MouseLeftButtonDown += MouseLeftButtonDown;
            AssociatedObject.MouseMove += MouseMove;
        }

        private void MouseLeftButtonDown(object sender, MouseEventArgs e)
        {
            mouseDown = true;
            oldMousePosition = AssociatedObject.PointToScreen(e.GetPosition(AssociatedObject));
            AssociatedObject.Child.CaptureMouse();
        }

        private void MouseMove(object sender, MouseEventArgs 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;
        }

        private void MouseLeftButtonUp(object sender, MouseEventArgs e)
        {
            mouseDown = false;
            AssociatedObject.Child.ReleaseMouseCapture();
        }
    }

就像 MapCanvas 一样,如果我们按下鼠标左键,移动鼠标,然后释放鼠标左键,我们希望弹出窗口在屏幕上拖动。相反,当我尝试左键单击并拖动时,MapCanvas 会被拖动,如下所示:

在这里,我正在看 map ;单击蓝点可打开弹出窗口。

我刚刚打开我的流行音乐,准备把它拖到一边

您可以看到,减去裁剪引起的抖动,我的弹出窗口保留在原位,而我的 map 已被拖动

在我的屏幕截图中没有显示的是一旦我松开鼠标左键弹出窗口的“粘性”行为:此时弹出窗口将开始拖动,我只能通过双击。

如果你已经做到了这一步,我祝贺你。我只能希望如此大的耐心可能与对我的问题的理解相吻合,并且您可能能够阐明这个错误。

最佳答案

嗯...我今天学到了一些东西。我不知道,在路由事件(可能还有其他事件)中,您可以通过将 EventArgs.Handled 设置为 true 来将事件标记为已处理。

下面的代码解决了我所有的问题:

        private void MouseLeftButtonDown(object sender, MouseEventArgs e)
        {
            mouseDown = true;
            oldMousePosition = AssociatedObject.PointToScreen(e.GetPosition(AssociatedObject));
            AssociatedObject.Child.CaptureMouse();
            e.Handled = true;
        }

这一行简单的新代码可以防止其他任何人获得 MouseLeftButtonDown 事件。

关于c# - 我的可拖动弹出窗口也会拖动拥有它们的视觉元素。我怎样才能解决这个问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26766168/

相关文章:

c# - 在 C# 中使用相对路径的 SSIS 远程执行

c# - 我的 Visual Studio C# 编译需要多长时间?

c# - 如何在 Entity Framework 4 Designer 中分配默认值并定义唯一键

wpf - 用拇指拖动 wpf 窗口 : can it be transparent?

wpf - 绑定(bind)到复杂对象时不会触发 IDataErrorInfo

c# - 我可以在 string.Format 中格式化 NULL 值吗?

c# - 文档缓存仍在构建中...消息不会在 VS 2010 中消失

c# - 复制一个 ObservableCollection 项

wpf - 有人在Revit环境中使用WPF/MVVM吗?

WPF MVVM 用户控件绑定(bind)问题