如何在Canvas中拖动UserControl

lor*_*ris 14 c# wpf user-controls canvas drag

我正在编写我的第一个WPF应用程序.我有一个Canvas,用户可以在其中添加包含表单的UserControl子类.用户应该能够在Canvas周围拖动这些UserControl.使用WPF执行此操作的最佳做​​法是什么?谢谢.

Cor*_*old 40

这是在silverlight中完成的,而不是在WPF中完成的,但它应该工作相同.

在控件上创建两个私有属性:

protected bool isDragging;  
private Point clickPosition;
Run Code Online (Sandbox Code Playgroud)

然后在控件的构造函数中附加一些事件处理程序:

this.MouseLeftButtonDown += new MouseButtonEventHandler(Control_MouseLeftButtonDown);
this.MouseLeftButtonUp += new MouseButtonEventHandler(Control_MouseLeftButtonUp);
this.MouseMove += new MouseEventHandler(Control_MouseMove);
Run Code Online (Sandbox Code Playgroud)

现在创建这些方法:

private void Control_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    isDragging = true;
    var draggableControl = sender as UserControl;
    clickPosition = e.GetPosition(this);
    draggableControl.CaptureMouse();
}

private void Control_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    isDragging = false;
    var draggable = sender as UserControl;
    draggable.ReleaseMouseCapture();
}

private void Control_MouseMove(object sender, MouseEventArgs e)
{
    var draggableControl = sender as UserControl;

    if (isDragging && draggableControl != null)
    {
        Point currentPosition = e.GetPosition(this.Parent as UIElement);

        var transform = draggableControl.RenderTransform as TranslateTransform;
        if (transform == null)
        {
            transform = new TranslateTransform();
            draggableControl.RenderTransform = transform;
        }

        transform.X = currentPosition.X - clickPosition.X;
        transform.Y = currentPosition.Y - clickPosition.Y;
    }
}
Run Code Online (Sandbox Code Playgroud)

这里需要注意的几点:
1.这不一定要在画布上.它也可以位于堆叠面板或网格中.
2.这使整个控件可拖动,这意味着如果您单击控件中的任意位置并拖动它将拖动整个控件.不确定那是不是你想要的.

编辑 -
扩展你问题中的一些细节:我实现这个的最好方法是创建一个继承自UserControl的类,也许叫做使用此代码构建的DraggableControl,然后所有可拖动的控件都应该扩展DraggableControl.

编辑2 - 在此控件中有数据网格时,存在一个小问题.如果对数据网格中的列进行排序,则MouseLeftButtonUp事件永远不会触发.我已更新代码,以便isDragging受到保护.我发现最好的解决方案是将此匿名方法绑定到datagrid的LostMouseCapture事件:

this.MyDataGrid.LostMouseCapture += (sender, e) => { this.isDragging = false; };
Run Code Online (Sandbox Code Playgroud)

  • 在`Control_MouseLeftButtonDown`中,我发现我需要用`clickPosition = e.GetPosition(this.Parent as UIElement)替换`clickPosition = e.GetPosition(this);`;`.在进行更改之前,我单击的元素会向下跳转(因为它在元素本身的范围内而不是托管画布中获得了起始位置). (4认同)
  • @loris:我很想知道你需要做出哪些改变以及为什么要做出改变.我已经在一些项目中使用了这个确切的代码,它运行良好.也许有一些我忽略的东西. (2认同)

Don*_*ott 5

科里的回答大部分是正确的,但它缺少一个关键要素:对上次转换的记忆。否则,当您移动一个项目时,松开鼠标按钮,然后再次单击该项目,变换将重置为(0,0)并且控件跳回其原点。

这是一个对我有用的稍微修改过的版本:

public partial class DragItem : UserControl
{
    protected Boolean isDragging;
    private Point mousePosition;
    private Double prevX, prevY;

    public DragItem()
    {
        InitializeComponent();
    }

    private void UserControl_MouseLeftButtonDown(Object sender, MouseButtonEventArgs e)
    {
        isDragging = true;
        var draggableControl = (sender as UserControl);
        mousePosition = e.GetPosition(Parent as UIElement);
        draggableControl.CaptureMouse();
    }

    private void UserControl_MouseLeftButtonUp(Object sender, MouseButtonEventArgs e)
    {
        isDragging = false;
        var draggable = (sender as UserControl);
        var transform = (draggable.RenderTransform as TranslateTransform);
        if (transform != null)
        {
            prevX = transform.X;
            prevY = transform.Y;
        }
        draggable.ReleaseMouseCapture();
    }

    private void UserControl_MouseMove(Object sender, MouseEventArgs e)
    {
        var draggableControl = (sender as UserControl);
        if (isDragging && draggableControl != null)
        {
            var currentPosition = e.GetPosition(Parent as UIElement);
            var transform = (draggableControl.RenderTransform as TranslateTransform);
            if (transform == null)
            {
                transform = new TranslateTransform();
                draggableControl.RenderTransform = transform;
            }
            transform.X = (currentPosition.X - mousePosition.X);
            transform.Y = (currentPosition.Y - mousePosition.Y);
            if (prevX > 0)
            {
                transform.X += prevX;
                transform.Y += prevY;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

关键是存储之前的 X 和 Y 偏移量,然后使用它们来增加当前移动的偏移量,以达到正确的聚合偏移量。