English 中文(简体)
How can I catch both single-click and double-click events on WPF FrameworkElement?
原标题:

I can catch a single-click on a TextBlock like this:

private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
    MessageBox.Show("you single-clicked");
}

I can catch a double-click on a TextBlock like this:

private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
    if (e.LeftButton == MouseButtonState.Pressed)
    {
        if (e.ClickCount == 2)
        {
            MessageBox.Show("you double-clicked");
        }
    }
}

But how do I catch them both on a single TextBlock and differentiate between the two?

问题回答

You need to fire the event after the click sequence is over... when is that? I suggest using a timer. The MouseDown event would reset it and increase the click count. When timer interval elapses it makes the call to evaluate the click count.

    private System.Timers.Timer ClickTimer;
    private int ClickCounter;

    public MyView()
    {
        ClickTimer = new Timer(300);
        ClickTimer.Elapsed += new ElapsedEventHandler(EvaluateClicks);
        InitializeComponent();
    }

    private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
    {
        ClickTimer.Stop();
        ClickCounter++;
        ClickTimer.Start();
    }

    private void EvaluateClicks(object source, ElapsedEventArgs e)
    {
        ClickTimer.Stop();
        // Evaluate ClickCounter here
        ClickCounter = 0;
    }

Cheers!

If you need to detect the difference, I suggest you use a control such as Label that does the work for you:

label.MouseDown += delegate(object sender, MouseEventArgs e)
{
    if (e.ClickCount == 1)
    {
        // single click
    }
};

label.MouseDoubleClick += delegate
{
    // double click
};

EDIT: My advice was following from documentation on MSDN:

The Control class defines the PreviewMouseDoubleClick and MouseDoubleClick events, but not corresponding single-click events. To see if the user has clicked the control once, handle the MouseDown event (or one of its counterparts) and check whether the ClickCount property value is 1.

However, doing so will give you a single click notification even if the user single clicks.

You must use a timer to differentiate between the two. Add a timer to your form in the GUI (easiest that way - it will automatically handle disposing etc...). In my example, the timer is called clickTimer.

private bool mSingleClick;

private void TextBlock_MouseUp(object sender, MouseButtonEventArgs e)
{

    if (e.Button == MouseButtons.Left)
    {
        if (e.ClickCount < 2)
        {
            mSingleClick = true;
            clickTimer.Interval = System.Windows.Forms.SystemInformation.DoubleClickTime;
            clickTimer.Start();
        }
        else if (e.ClickCount == 2)
        {
            clickTimer.Stop();
            mSingleClick = false;
            MessageBox.Show("you double-clicked");
        }
    }
}

private void clickTimer_Tick(object sender, EventArgs e)
{
    if (mSingleClick)
    {
        clickTimer.Stop();
        mSingleClick = false;
        MessageBox.Show("you single-clicked");
    }
}

I did it this Way and it works perfectly

If e.Clicks = 2 Then
            doubleClickTimer.Stop()
        ElseIf e.Clicks = 1 Then
            doubleClickTimer.Enabled = True
            doubleClickTimer.Interval = 1000
            doubleClickTimer.Start()


        End If


 Private Sub doubleClickTimer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles doubleClickTimer.Tick

        OpenWebPage("abc")
        doubleClickTimer.Stop()
    End Sub

You are simply can use MouseDown event and count click number, like this:

if (e.ChangedButton == MouseButton.Left && e.ClickCount == 2)
{
    // your code here
}

You could do it on MouseUp instead of MouseDown. That way you can ask the ClickCount property for the total number of clicks, and decide what to do from that point.

It s my working solution :)

    #region message label click --------------------------------------------------------------------------
private Timer messageLabelClickTimer = null;

private void messageLabel_MouseUp(object sender, MouseButtonEventArgs e)
{
  Debug.Print(e.ChangedButton.ToString() + " / Left:" + e.LeftButton.ToString() + "  Right:" + e.RightButton.ToString() + "  click: " + e.ClickCount.ToString());
  // in MouseUp (e.ClickCount == 2) don t work!!  Always 1 comes.
  // in MouseDown is set e.ClickCount succesfully  (but I don t know should I fire one clicked event or wait second click)

  if (e.ChangedButton == MouseButton.Left)
  {
    if (messageLabelClickTimer == null)
    {
      messageLabelClickTimer = new Timer();
      messageLabelClickTimer.Interval = 300;
      messageLabelClickTimer.Elapsed += new ElapsedEventHandler(messageLabelClickTimer_Tick);
    }

    if (! messageLabelClickTimer.Enabled)                                                 
    { // Equal: (e.ClickCount == 1)
      messageLabelClickTimer.Start();
    }
    else  
    { // Equal: (e.ClickCount == 2)
      messageLabelClickTimer.Stop();

      var player = new SoundPlayer(ExtraResource.bip_3short);               // Double clicked signal
      player.Play();
    }
  }
}

private void messageLabelClickTimer_Tick(object sender, EventArgs e)
{ // single-clicked
  messageLabelClickTimer.Stop();

  var player = new SoundPlayer(ExtraResource.bip_1short);                  // Single clicked signal
  player.Play();
}    
#endregion

My issue was with single/double-clicking rows in a DataGrid in WPF. For some reason the ButtonDown events weren t firing, only the OnMouseLeftButtonUp event was. Anyway, I wanted to handle the single-click differently from the double-click. It looks me a little time (I m sure the solution isn t perfect, but it appears to work) to distill the problem down until I got it down to the below. I created a Task which calls an Action and that Action s target can be updated by a second click. Hope this helps someone!

private Action _clickAction;
private int _clickCount;

private void Grid_OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    Debug.WriteLine("Button Click Occurred");

    _clickCount++;

    if (_clickCount == 1)
    {
        _clickAction = SingleClick;
    }

    if (_clickCount > 1)
    {
        _clickAction = DoubleClick;
    }

    if (_clickCount == 1)
    {
        Task.Delay(200)
            .ContinueWith(t => _clickAction(), TaskScheduler.FromCurrentSynchronizationContext())
            .ContinueWith(t => { _clickCount = 0; });
    }
}

private void DoubleGridClick()
{
    Debug.WriteLine("Double Click");
}

private void SingleGridClick()
{
    Debug.WriteLine("Single Click");
}

My suggestion, implemented in a UserControl by simply using a Task:

private int _clickCount = 0;
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
    _clickCount = e.ClickCount;
}

protected override async void OnPreviewMouseUp(MouseButtonEventArgs e)
{
    if (_clickCount > 1)
    {
        //apparently a second mouse down event has fired => this must be the second mouse up event
        //no need to start another task
        //the first mouse up event will be handled after the task below
        return; 
    }

    await Task.Delay(500);

    if (_clickCount == 1)
    {
        //single click
    }
    else
    {
        //double (or more) click
    }
}

The drawback of all these solutions is, of course, that there will be a delay before actually responding to the user s action.





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签