【问题标题】:C# WPF mouse click eventC# WPF 鼠标点击事件
【发布时间】:2018-06-29 02:48:49
【问题描述】:

我在 WPF 中使用 C#。我有一个按钮网格,我需要执行以下操作:如果用户按下一个按钮,移动光标并将其释放到另一个按钮上,则第一个按钮的内容将移动到另一个按钮,类似于拖动。

我尝试使用 previewmousedown 和 previewmouseup 按钮事件来了解鼠标按下的是哪个按钮以及释放的是哪个按钮,但是在按下鼠标的按钮上也会触发 previewmouseup 事件(不是在那个按钮上)鼠标被释放)。

请您对如何以其他方式实现这一点有任何想法?提前非常感谢。

【问题讨论】:

  • 您可能想尝试MouseLeftButtonDownMouseLeftButtonUp 事件!
  • 这些按钮是否需要可点击可拖动?

标签: c# wpf events button click


【解决方案1】:

进行拖放的最简单方法是使用内置的DragDrop API。这是一个概念证明,可以正常点击按钮*和*拖动以交换其内容。

如果您想更改行为以复制或移动内容(而不是交换),只需更改 OnButtonDrop 中注释下的行。

ButtonDragging.xaml:

<Window x:Class="WpfTest2.ButtonDragging"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <DockPanel LastChildFill="True">
    <Label x:Name="_statusLabel" DockPanel.Dock="Bottom" Content=" " />
    <Grid x:Name="_grid" />
  </DockPanel>
</Window>

ButtonDragging.xaml.cs:

public partial class ButtonDragging
{
    private Button _mouseDownButton;
    private Point _mouseDownLocation;

    public ButtonDragging()
    {
        InitializeComponent();
        BuildButtonGrid();
    }

    private void BuildButtonGrid()
    {
        const int rows = 5;
        const int columns = 5;

        var starLength = new GridLength(1d, GridUnitType.Star);

        for (var i = 0; i < rows; i++)
            _grid.RowDefinitions.Add(new RowDefinition { Height = starLength });

        for (var i = 0; i < columns; i++)
            _grid.ColumnDefinitions.Add(new ColumnDefinition { Width = starLength });

        for (var i = 0; i < rows; i++)
        {
            for (var j = 0; j < columns; j++)
            {
                var button = new Button { Content = $@"({i}, {j})", AllowDrop = true };

                Grid.SetColumn(button, i);
                Grid.SetRow(button, j);

                button.PreviewMouseMove += OnButtonMouseMove;
                button.PreviewMouseLeftButtonDown += OnButtonLeftButtonDown;
                button.PreviewMouseLeftButtonUp += OnButtonLeftButtonUp;
                button.Drop += OnButtonDrop;
                button.Click += OnButtonClick;
                button.LostMouseCapture += OnButtonLostMouseCapture;

                _grid.Children.Add(button);
            }
        }
    }

    private void OnButtonClick(object sender, RoutedEventArgs e)
    {
        _statusLabel.Content = $@"You clicked {(sender as Button)?.Content}!";
    }

    private void ClearPendingDrag()
    {
        _mouseDownButton = null;
        _mouseDownLocation = default(Point);
    }

    private void OnButtonDrop(object sender, DragEventArgs e)
    {
        ClearPendingDrag();

        var source = e.Data.GetData(typeof(object)) as Button;
        if (source == null)
            return;

        var target = (Button)sender;
        if (target == source)
            return;

        var sourceContent = source.Content;
        var targetContent = target.Content;

        // As a proof of concept, this swaps the content of the source and target.
        // Change as necessary to get the behavior you want.

        target.Content = sourceContent;
        source.Content = targetContent;

        _statusLabel.Content = $@"You swapped {sourceContent} with {targetContent}!";
    }

    private void OnButtonLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        var button = (Button)sender;

        _mouseDownButton = button;
        _mouseDownLocation = e.GetPosition(button);

        if (!Mouse.Capture(button, CaptureMode.SubTree))
            ClearPendingDrag();
    }

    private void OnButtonLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        ClearPendingDrag();
    }

    private void OnButtonMouseMove(object sender, MouseEventArgs e)
    {
        if (_mouseDownButton == null)
            return;

        var position = e.GetPosition(_mouseDownButton);
        var distance = position - _mouseDownLocation;

        if (Math.Abs(distance.X) > SystemParameters.MinimumHorizontalDragDistance ||
            Math.Abs(distance.Y) > SystemParameters.MinimumVerticalDragDistance)
        {
            var button = (Button)sender;
            var data = new DataObject(typeof(object), button);

            data.SetData("Source", sender);

            DragDrop.DoDragDrop(button, data, DragDropEffects.Move);

            ClearPendingDrag();
        }
    }

    private void OnButtonLostMouseCapture(object sender, MouseEventArgs e)
    {
        ClearPendingDrag();
    }
}

请注意,可能有一些第三方(和开源)拖放解决方案对 MVVM 更友好。值得一试,但这应该会为您提供最低限度的可行交付物。

【讨论】:

    【解决方案2】:

    看看这篇博文。这是我在网上阅读的最好的拖放文章。

    下面的链接是拖放操作的一系列事件:

    1. 通过调用源代码管理的DoDragDrop 方法来启动拖动。 DoDragDrop 方法有两个参数:

      data,指定要传递的数据

      allowedEffects,指定允许哪些操作(复制和/或移动) 将自动创建一个新的 DataObject 对象。

    2. 这又会引发GiveFeedback 事件。在大多数情况下,您无需担心GiveFeedback 事件,但如果您想在拖动过程中显示自定义鼠标指针,您可以在此处添加代码。

    3. 任何将其AllowDrop 属性设置为True 的控件都是潜在的放置目标。 AllowDrop 属性可以在设计时在“属性”窗口中设置,也可以在Form_Load 事件中以编程方式设置。

    4. 当鼠标经过每个控件时,会引发该控件的DragEnter 事件。 GetDataPresent 方法用于确保数据的格式适合目标控件,而 Effect 属性用于显示适当的鼠标指针。

    5. 如果用户在有效的放置目标上释放鼠标按钮,则会引发 DragDrop 事件。 DragDrop 事件处理程序中的代码从DataObject 对象中提取数据并将其显示在目标控件中。

    要检测源上的鼠标拖动操作,源控制需要订阅MouseLeftButtonDownMouseMove

    void Window1_Loaded(object sender, RoutedEventArgs e)
         {
             this.DragSource.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(DragSource_PreviewMouseLeftButtonDown);
             this.DragSource.PreviewMouseMove += new MouseEventHandler(DragSource_PreviewMouseMove);
         }
    

    为防止在用户意外拖动的情况下启动错误的拖放操作,您可以使用SystemParameters.MinimumHorizontalDragDistanceSystemParameters.MinimumVerticalDragDistance

    void DragSource_PreviewMouseMove(object sender, MouseEventArgs e)
            {
                if (e.LeftButton == MouseButtonState.Pressed && !IsDragging)
                {
                    Point position = e.GetPosition(null);
    
                    if (Math.Abs(position.X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
    
                        Math.Abs(position.Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
    
                    {
                      StartDrag(e); 
                    }
                }
            }
    
            void DragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                _startPoint = e.GetPosition(null);
            }
    

    现在您检测到拖动操作,您只需要知道拖放即可。

    一个没有任何效果的简单场景可以这样完成。

    private void StartDrag(MouseEventArgs e)
            {
                IsDragging = true;
    
                DataObject data = new DataObject(System.Windows.DataFormats.Text.ToString(), "abcd");
    
                DragDropEffects de = DragDrop.DoDragDrop(this.DragSource, data, DragDropEffects.Move);
    
                IsDragging = false;
            }
    

    我认为这可以帮助您入门。我真的建议阅读链接中的完整帖子。

    https://blogs.msdn.microsoft.com/jaimer/2007/07/12/drag-amp-drop-in-wpf-explained-end-to-end/

    【讨论】: