实现WaitForMouseUp()函数的更好方法?

时间:2016-01-05 05:29:16

标签: c# winforms

我需要一个小功能,等待释放左侧按钮,而不是基于MouseUp event

在许多情况下,当我们需要这个时,我们只需为MouseUp event编写一个事件处理程序 它很简单,而且很有效。

然而,有些情况下使用MouseUp event无效,
例如,当我们已经在另一个(不同的)事件处理程序中时, 调用此事件处理程序时可能会按下鼠标左键,我们需要等待它被释放。 (目标是拥有一个代码流,而不必在几个可能已被其他代码占用的地方之间拆分)

我是这样实现的:

public void WaitForMouseUp()
{
    while( (Control.MouseButtons&MouseButtons.Left)!=0 )
        Application.DoEvents();
}

它有效,
例如,当您在Control.Enter事件的事件处理程序中时,可以使用它 如果通过鼠标输入控件,则此功能将被阻止,直到释放鼠标按钮。

我只担心一件事:
我在那里使用Application.DoEvents(),我想知道是否有其他方式而不是使用Application.DoEvents()。 (Application.DoEvents();具有可能的重入性的缺点,因此,因此我尽量减少使用它,尽可能减少使用它)

任何人都知道我可以替换Application.DoEvents()部分吗?

4 个答案:

答案 0 :(得分:2)

这是一个很棒的方式来做你要问的事情。使用Microsoft的Reactive Extensions使一行代码可以执行您想要的任何操作。

反应式扩展提供了大量可应用于事件的运算符。

首先是一些与正常控制事件直接相关的基本可观察量:

        var mouseEnters =
            Observable
                .FromEventPattern(
                    h => button1.MouseEnter += h,
                    h => button1.MouseEnter -= h);

        var mouseLeaves =
            Observable
                .FromEventPattern(
                    h => button1.MouseLeave += h,
                    h => button1.MouseLeave -= h);

        var mouseUps =
            Observable
                .FromEventPattern<MouseEventHandler, MouseEventArgs>(
                    h => button1.MouseUp += h,
                    h => button1.MouseUp -= h);

现在我们需要一个只在鼠标出现时才会触发一次的查询,但前提是鼠标已经输入button1,但只有在它离开之前。

        var query =
            mouseEnters
                .Select(me => mouseUps.Take(1).TakeUntil(mouseLeaves))
                .Switch();

现在订阅该事件以便能够处理它:

        var subscription =
            query
                .Subscribe(ep =>
                {
                    /*
                        this code runs for the first mouse up only
                        after each mouse enter on `button1`
                        unless the mouse leaves `button1`
                    */
                });

现在因为subscription的类型为IDisposable而取消订阅非常简单。所以你只需致电subscription.Dispose();

只需NuGet“Rx-WinForms”来获取项目的位数。

答案 1 :(得分:2)

事实上@Kai Brummund建议的是我answerForce loop to wait for an event的变体。从那里为MouseUp调整代码很简单,如

public static class Utils
{
    public static Task WhenMouseUp(this Control control)
    {
        var tcs = new TaskCompletionSource<object>();
        MouseEventHandler onMouseUp = null;
        onMouseUp = (sender, e) =>
        {
            control.MouseUp -= onMouseUp;
            tcs.TrySetResult(null);
        };
        control.MouseUp += onMouseUp;
        return tcs.Task;
    }
}

,用法是

Control c = ...;
await c.WhenMouseUp();

同样的技术可以用于任何事件。

答案 2 :(得分:1)

如果您不想在单个方法中编写流程,则可以使用TaskCompletionSource进行等待。

你的流程:

await MouseUp();

...

private Task MouseUp() {
  _tcs = new TaskCompletionSource();
  return _tcs.Task;
}

public ... OnMouseUpEvent() {
  _tcs?.SetResult(true);
}

对于伪代码感到抱歉,一旦我得到移动设备以外的其他内容,我们就会更新。

OT:评论者:在盒子外面思考!

答案 3 :(得分:0)

  

我需要一个小功能,等待鼠标的左键被释放。

不,不。 WinForms GUI编程是事件驱动的,异步的。您应该使用MouseUp事件来检测鼠标按钮的释放。这意味着您需要使用基于状态的异步技术来实现逻辑,而不是您渴望的同步模型。