【问题标题】:How to cancel a left mouseclick on button release (Preview...Up)如何取消鼠标左键单击按钮释放(预览...向上)
【发布时间】:2014-05-01 17:55:43
【问题描述】:

我正在开发一个 dragScrollViewer,在 OnPreviewMouseLeftButtonUp 函数中使用“e.Handled=true”时遇到了一些奇怪的问题。

在鼠标左键下,不知道用户是想“点击”下方还是想用鼠标“滑动/拖动”。如果他已经开始“滑动/拖动”,我想“吃/取消”鼠标点击。

这应该很简单...... OnPreviewMouseLeftButtonUp 函数中的“e.Handled = true”应该会阻止鼠标点击点击更高级别的按钮(鼠标下方)。然而,这会产生一个非常奇怪的行为......点击(及其坐标)被存储并在稍后(用户下次点击时)抛出。

我不知道我的代码是否有问题,或者 WPF 路由事件框架中是否存在错误...是否有人能够重现该问题? (为了让代码更简单,我把所有拖拽的代码都去掉了)

如何重现问题:

  1. 在 Visual Studio 中创建一个干净的项目 WPF 项目。
  2. 插入示例源代码 DragScroller.cs/MainWindow.xaml/MainWindow.xaml.cs
  3. 编译并单击按钮 - 结果:控制台显示“单击了内部 dragScroller 按钮”
  4. 接下来单击一个按钮并开始滑动(仍停留在按钮区域内 - 结果:鼠标单击被取消
  5. 现在单击“外部 dragScroller” - 结果:控制台写入“内部 dragScroller 按钮单击”(这是存储的“鼠标”单击)

如果取消单击的决定是在用户释放鼠标按钮时首先知道的,是否有更好的方法来取消鼠标单击?


DragScroller.cs:

    using System.Windows.Controls;
    using System.Windows.Input;

    namespace dragScroller
    {
      public class DragScrollViewer : ScrollViewer
        {    
            private bool mouseDown;
            private bool isDragging;
            private int dragMoveCount;

          protected override void OnMouseLeave(MouseEventArgs e)
          {
              base.OnMouseLeave(e);
            CancelMouseDrag();
          }

            protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
            {
                base.OnPreviewMouseLeftButtonDown(e);
                dragMoveCount = 0;      
                mouseDown = true;
            }

            protected override void OnPreviewMouseMove(MouseEventArgs e)
            {
                base.OnPreviewMouseMove(e);      
                dragMoveCount++;
                if (!mouseDown || isDragging || !(dragMoveCount > MoveTicksBeforeDrag)) return;
                Cursor = Cursors.ScrollAll;
                isDragging = true;
            }

            protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
            {
                base.OnPreviewMouseLeftButtonUp(e);

                if (isDragging && dragMoveCount > MoveTicksBeforeDrag)
                {
                    e.Handled = true;// Calling e.Handled here, has an unwanted effect on the next "up" event               
                    CancelMouseDrag();        
                }
                dragMoveCount = 0;
                Cursor = Cursors.Arrow;
            }    

            private void CancelMouseDrag()
            {
                isDragging = false;
                mouseDown = false;
                Cursor = Cursors.Arrow;
                dragMoveCount = 0;
            }    

            private const double MoveTicksBeforeDrag = 5; //times to call previewMouseMove before starting to drag (else click)   
        }
    }

MainWindows.xaml:

<Window x:Class="dragScroller.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:dragScroller="clr-namespace:dragScroller"
        Title="MainWindow" Height="350" Width="525">

  <StackPanel>
    <Button Content="Outside dragScroller" Click="Button_Click" />
    <dragScroller:DragScrollViewer x:Name="dragScroller"  Friction="0.2" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Visible">
     <Grid>
      <Grid.RowDefinitions>
         <RowDefinition Height="100" />
        <RowDefinition Height="100" />
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
           <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="100"/>
      </Grid.ColumnDefinitions>
        <Button Content="Button #1" Grid.Column="0" Grid.Row="0" Click="Button_Click_Inside_DragScroller"></Button>
        <Button Content="Button #2" Grid.Column="1" Grid.Row="0" Click="Button_Click_Inside_DragScroller"></Button>
        <Button Content="Button #3" Grid.Column="2" Grid.Row="1" Click="Button_Click_Inside_DragScroller"></Button>
        <Button Content="Button #4" Grid.Column="3" Grid.Row="1" Click="Button_Click_Inside_DragScroller"></Button>
        <Button Content="Button #5" Grid.Column="4" Grid.Row="0" Click="Button_Click_Inside_DragScroller"></Button>
        <Button Content="Button #6" Grid.Column="5" Grid.Row="0" Click="Button_Click_Inside_DragScroller"></Button>
        <Button Content="Button #7" Grid.Column="6" Grid.Row="1" Click="Button_Click_Inside_DragScroller"></Button>
        <Button Content="Button #8" Grid.Column="7" Grid.Row="1" Click="Button_Click_Inside_DragScroller"></Button>
    </Grid>
    </dragScroller:DragScrollViewer>
  </StackPanel>
</Window>

MainWindows.xaml.cs:

namespace dragScroller
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
      Debug.WriteLine("Outside dragScroller button clicked");
    }

    private void Button_Click_Inside_DragScroller(object sender, RoutedEventArgs e)
    {
      Debug.WriteLine("Inside dragScroller button clicked");
    }
  }
}

【问题讨论】:

    标签: wpf wpf-controls routed-events


    【解决方案1】:

    我修改了 OnPreviewMouseLeftButtonUp 方法并设法修复了此行为。 调用了错误的事件处理程序,因为按钮仍处于焦点。 如果您将焦点移回主窗口,它应该可以按预期工作:

     protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            base.OnPreviewMouseLeftButtonUp(e);
    
            if (isDragging && dragMoveCount > MoveTicksBeforeDrag)
            {
                e.Handled = true;// Calling e.Handled here, has an unwanted effect on the next "up" event       
                var x = e.Source as Button;
                if (x != null)
                {
                    FocusManager.SetFocusedElement(FocusManager.GetFocusScope(x), Application.Current.MainWindow);
                }
                CancelMouseDrag();
            }
            dragMoveCount = 0;
            Cursor = Cursors.Arrow;
        }
    

    希望对你有帮助。

    【讨论】:

    • 感谢您的意见,沙哈尔。对此,我真的非常感激。当我插入您的“FocusManager”-sn-p 代码时,我最初在(e.Source as Button)上得到一个 NullException。插入此代码“var x = e.Source as Button; if (x != null) FocusManager.SetFocusedElement(FocusManager.GetFocusScope(x), Application.Current.MainWindow);”而是工作,但不幸的是,它不再可能单击任何“内部”按钮。
    • 它在我的电脑上工作,按钮被点击,除非使用做一些拖动——然后点击被取消并且焦点返回到主窗口。
    • 你是对的,它确实有效!不知何故,“鼠标点击”挂在按钮(UIElement)上,从 UIElement 释放焦点解决了这个问题。在我的实际项目中,我不仅使用按钮,还使用其他 UIElement,因此使用“var x = e.Source as UIElement”可以解决问题。然而,在某种程度上,这似乎是一种解决方法——我想可能有更好的方法来解决这个问题,所以 UIElement 永远不会得到关注。但是它有效,我非常高兴你的帮助,shahar。谢谢! :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多