【问题标题】:WPF C# How to Make Control Draggable with MouseWPF C#如何使用鼠标使控件可拖动
【发布时间】:2019-09-11 06:06:44
【问题描述】:

我正在尝试制作一个在画布上拖动的 UserControl。我正在使用 C# 和 WPF。我在网上看到很多例子,但我只需要最低限度的。

找到一篇文章:《WPF中的可拖动控件》

有人回复:

如果您想手动操作,请使用以下算法:

在 MouseDown 事件中:保存鼠标位置、控件的 TopLeft 位置和这些坐标的 delta(offset),并设置一些布尔字段标志,例如。 IsDragStartted 为真。 在 MouseMove 上检查是否开始拖动并使用鼠标位置和偏移量来计算控件的 TopLeft 位置的新值

在 MouseUp 事件中将 IsDragStarted 设置为 false


我无法应用这个。

公共部分类 UserControl1:UserControl {

    private Point startingMousePosition;
    private Point endingMousePosition;
    private Point startingControlPosition;
    bool isDragStarted;

    public UserControl1()
    {
        InitializeComponent();

    }

    private void Grid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if(!isDragStarted)
        {
            startingControlPosition.X = Canvas.GetLeft(this);
            startingControlPosition.Y = Canvas.GetTop(this);

            startingMousePosition.X = e.GetPosition(this.Parent as Canvas).X;
            startingMousePosition.Y = e.GetPosition(this.Parent as Canvas).Y;
        }



    }

    private void Grid_PreviewMouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed)
        {
            isDragStarted = true;

            if (isDragStarted)
            {

                endingMousePosition.X = e.GetPosition(this.Parent as Canvas).X;
                endingMousePosition.Y = e.GetPosition(this.Parent as Canvas).Y;

                Canvas.SetLeft(this, endingMousePosition.X - startingControlPosition.X);
                Canvas.SetTop(this, endingMousePosition.Y - startingControlPosition.Y);

            }
        }

    }



    private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {

        isDragStarted = false;
    }



}

这是我的主窗口 WPF 表单代码:

{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{

    UserControl1 userCTL;
    public MainWindow()
    {
        InitializeComponent();

        userCTL = new UserControl1();
        userCTL.Width = 50;
        userCTL.Height = 100;
        Canvas.SetTop(userCTL,20);
        Canvas.SetLeft(userCTL, 20);

        CanvasMain.Children.Add(userCTL);

    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        AdornerLayer myAdornerLayer = AdornerLayer.GetAdornerLayer(userCTL);
        if (myAdornerLayer != null)
        {
            myAdornerLayer.Add(new SimpleCircleAdorner(userCTL));
        } 
    }




}

}

这是我的主窗口的 WPF 代码:

<Window x:Class="WpfApplicationEvent.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="374.306" Width="594.271" Loaded="Window_Loaded">
<Grid>
    <ScrollViewer Margin="46,23,74,33" PanningMode="Both" HorizontalScrollBarVisibility="Visible">
        <Canvas x:Name="CanvasMain" Height="395" Width="506">

        </Canvas>
    </ScrollViewer>

</Grid>
</Window>

我还尝试放置装饰器,以便最终调整控件的大小。与装饰者一样无处可去,但他们什么也没做。

我在我创建的 UserControl1 中有我所有的拖动控件,我可以拖动它,但是当我再次单击 UserControl1 实例以再次拖动它时,它会重置为 SetTop(0) 和 SetLeft (0) 位置。它跳到那里太奇怪了!我期待将 UserControl1 实例拖到光标的位置。它在第一次尝试时执行,但随后我单击 UserControl1 再次拖动它,它会跳转到 (0,0) 或接近它。

【问题讨论】:

    标签: c# wpf canvas draggable drag


    【解决方案1】:

    这里有几个问题...

    1. PreviewMouseDown 事件处理程序应该添加到您的子控件中,否则您将不知道您正在拖动哪个对象。
    2. 您将在父 Canvas 上拖动,因此请将 MouseMove 处理程序添加到其中。
    3. 再次在 Canvas 控件上调用 CaptureMouse(),并在拖动过程中保持捕获。
    4. 计算鼠标相对于您拖动的控件左上角的位置,然后在每次设置位置时反向应用该偏移量。这会使您在拖动过程中单击鼠标光标的点保持不变,并阻止它“跳”到新位置,这对用户来说很烦人。

    所以您的画布 XAML 现在应该如下所示:

    <Canvas x:Name="CanvasMain" Height="395" Width="506"
        PreviewMouseMove="CanvasMain_PreviewMouseMove"
        PreviewMouseUp="CanvasMain_PreviewMouseUp" />
    

    您的代码隐藏应该如下所示:

    public MainWindow()
    {
        InitializeComponent();
    
        var userCTL = new UserControl();    // <-- replace with your own control
        userCTL.Background = Brushes.Blue;  // <-- added this so I can see it
        userCTL.Width = 50;
        userCTL.Height = 100;
        Canvas.SetTop(userCTL, 20);
        Canvas.SetLeft(userCTL, 20);
        userCTL.PreviewMouseDown += UserCTL_PreviewMouseDown;
    
        CanvasMain.Children.Add(userCTL);
    }
    
    UIElement dragObject = null;
    Point offset;
    
    private void UserCTL_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        this.dragObject = sender as UIElement;
        this.offset = e.GetPosition(this.CanvasMain);
        this.offset.Y -= Canvas.GetTop(this.dragObject);
        this.offset.X -= Canvas.GetLeft(this.dragObject);
        this.CanvasMain.CaptureMouse();
    }
    
    private void CanvasMain_PreviewMouseMove(object sender, MouseEventArgs e)
    {
        if (this.dragObject == null)
            return;
        var position = e.GetPosition(sender as IInputElement);
        Canvas.SetTop(this.dragObject, position.Y - this.offset.Y);
        Canvas.SetLeft(this.dragObject, position.X - this.offset.X);
    }
    
    private void CanvasMain_PreviewMouseUp(object sender, MouseButtonEventArgs e)
    {
        this.dragObject = null;
        this.CanvasMain.ReleaseMouseCapture();
    }
    

    您可能还想向 Canvas 添加一个 MouseLeave 处理程序,以阻止用户将控件拖动到可见客户区之外。

    【讨论】:

    • 非常感谢。我只有几个问题。这个 dragObject 是什么,为什么要使用它?
    • 另外你怎么把对象转换成“FrameworkElement”? CanvasMain.CaptureMouse() 做什么以及为什么使用它?还有“ReleaseMouseCapture() 是做什么的?
    • dragObject 只是指向您当前正在拖动的对象,它与使用布尔值基本相同。当您稍后扩展拖动功能时,保留对实际对象本身的引用会派上用场。我最初确实将其设为 FrameworkElement,但将其更改为 IInputElement,因为这是 Canvas.SetTop 等所期望的。如果需要,您可以使用 bool 或将其设为您的自定义控件类型,但通常最好的编程习惯是引用最“基”的类或接口。
    • 捕获鼠标会导致所有鼠标事件消息被定向到捕获它的窗口,即使用户将光标移到窗口外也是如此。 ReleaseMouseCapture 只是将其恢复为正常状态,以便用户可以恢复与其他窗口等交互。尝试将这两行去掉,您会看到拖动行为有多么不同。
    猜你喜欢
    • 2011-06-22
    • 1970-01-01
    • 2021-06-27
    • 1970-01-01
    • 1970-01-01
    • 2013-06-30
    • 1970-01-01
    • 2013-04-29
    • 1970-01-01
    相关资源
    最近更新 更多