WPF StackPanel with Click AND DoubleClick

2019-03-28 09:45发布

I need to be able to handle the double click and single click event on the WPF StackPanel. But there is no such thing as the StackPanel's DoubleClick Event. I want to do 2 different operations in these 2 EventHandlers.

Any idea how to do that?

Thank you

7条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-03-28 10:16

The best way is to right your own mouse button handler with a timeout - if the event is fired again within the timeout period, then fire your doubleclick message, otherwise call the single click handler. Here's some sample code (Edit: originally found here):

/// <summary>
/// For double clicks
/// </summary>
public class MouseClickManager {
    private event MouseButtonEventHandler _click;
    private event MouseButtonEventHandler _doubleClick;

    public event MouseButtonEventHandler Click {
        add { _click += value; }
        remove { _click -= value; }
    }

    public event MouseButtonEventHandler DoubleClick {
        add { _doubleClick += value; }
        remove { _doubleClick -= value; }
    }

    /// <summary>
    /// Gets or sets a value indicating whether this <see cref="MouseClickManager"/> is clicked.
    /// </summary>
    /// <value><c>true</c> if clicked; otherwise, <c>false</c>.</value>
    private bool Clicked { get; set; }

    /// <summary>
    /// Gets or sets the timeout.
    /// </summary>
    /// <value>The timeout.</value>
    public int DoubleClickTimeout { get; set; }

    /// <summary>
    /// Initializes a new instance of the <see cref="MouseClickManager"/> class.
    /// </summary>
    /// <param name="control">The control.</param>
    public MouseClickManager(int doubleClickTimeout) {
        this.Clicked = false;
        this.DoubleClickTimeout = doubleClickTimeout;
    }

    /// <summary>
    /// Handles the click.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
    public void HandleClick(object sender, MouseButtonEventArgs e) {
        lock (this) {
            if (this.Clicked) {
                this.Clicked = false;
                OnDoubleClick(sender, e);
            }
            else {
                this.Clicked = true;
                ParameterizedThreadStart threadStart = new ParameterizedThreadStart(ResetThread);
                Thread thread = new Thread(threadStart);
                thread.Start(e);
            }
        }
    }

    /// <summary>
    /// Resets the thread.
    /// </summary>
    /// <param name="state">The state.</param>
    private void ResetThread(object state) {
        Thread.Sleep(this.DoubleClickTimeout);

        lock (this) {
            if (this.Clicked) {
                this.Clicked = false;
                OnClick(this, (MouseButtonEventArgs)state);
            }
        }
    }

    /// <summary>
    /// Called when [click].
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
    private void OnClick(object sender, MouseButtonEventArgs e) {
        if (_click != null) {
            if (sender is Control) {
                (sender as Control).Dispatcher.BeginInvoke(_click, sender, e);
            }
        }
    }

    /// <summary>
    /// Called when [double click].
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
    private void OnDoubleClick(object sender, MouseButtonEventArgs e) {
        if (_doubleClick != null) {
            _doubleClick(sender, e);
        }
    }
}

Then, in the control you want to receive the events:

MouseClickManager fMouseManager = new MouseClickManager(200);
fMouseManager.Click += new MouseButtonEventHandler(YourControl_Click); 
fMouseManager.DoubleClick += new MouseButtonEventHandler(YourControl_DoubleClick);
查看更多
趁早两清
3楼-- · 2019-03-28 10:16
 <StackPanel MouseDown="StackPanel_MouseDown">
   <!--stackpanel content-->
    <TextBlock>Hello</TextBlock>
</StackPanel>

Then in the event handler:

 private void StackPanel_MouseDown(object sender, MouseButtonEventArgs e)
    {
        if (e.ClickCount >= 2)
        { 
            string hello; //only hit here on double click  
        }
    }

Should work. Note that single clicking in the StackPanel would hit the event (but fail the if check)

查看更多
forever°为你锁心
4楼-- · 2019-03-28 10:17

...years later. @MoominTroll's solution is perfectly acceptable. Another option is to wrap the stack panel in a content control that supports the double click event.

<ContentControl MouseDoubleClick="DoubleClickHandler" >
    <StackPanel>

    </StackPanel>
</ContentControl>
查看更多
ら.Afraid
5楼-- · 2019-03-28 10:22

Another option is to add a MouseBinding to the InputBindings on the StackElement and then add a CommandBinding that gets activated by MouseBinding. On the whole this is a better practice than event based mechanisms because it avoids the memory leak issues caused by strong references. It also provides for separation of command logic from the representation.

That being said, its not as straight forward and attaching to the event makes for a great shortcut.

And it goes without saying, make your stackpanel background at least transparent or it won't be caught by the mouse click hit test when you click on the "background". Null backgrounds are skipped over by hit detection.

查看更多
时光不老,我们不散
6楼-- · 2019-03-28 10:26

I had a similar problem (respond to single click event and do an additional work in case of double click event). I solved the problem this way:

1) define and declare some integer to hold last click timestamp

int lastClickTimestamp;

2) in Window_Loaded method initialize previously declared variable with some number greater than 200

lastClickTimestamp = 1000;

3) add mouse button handler to your stack panel

stackPanel.MouseLeftButtonUp += new MouseButtonEventHandler(stackPanel_MouseLeftButtonUp);

4) add following method

void stackPanel_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        if (e.Timestamp - lastClickTimeStamp < 200)
        {
            //double click
        }
        lastClickTimeStamp = e.Timestamp;

        //single click
    }

This code is useless if you need to separately detect single click and double click event. That situation introduces a bit more complexity, but definitly can be solved.

查看更多
贼婆χ
7楼-- · 2019-03-28 10:26

There is an easier solution to do this.

In StackPanel's event PreviewMouseLeftDown (for example), you can check if MouseButtonEventArgs.ClickCount property has a value of 2. 1 = Single click 2 = Double Click

查看更多
登录 后发表回答