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

C# 实现WaitForMouseUp()函数的更好方法?,c#,winforms,C#,Winforms,我需要一个小功能,它将等待鼠标左键被释放,并且不会基于MouseUp事件 在许多情况下,当我们需要它时,我们只需为MouseUp事件编写一个事件处理程序 这很简单,而且很有效 但是,在某些情况下,使用MouseUp事件将不会有用, 例如,当我们已经在另一个不同的事件处理程序中时, 调用此事件处理程序时,可能会按下鼠标左键,我们需要等待它被释放。 (目标是要有一个单一的代码流,而不必在几个可能已经被另一个代码占用的地方进行分割) 我是这样实施的: public void WaitForMouseU

我需要一个小功能,它将等待鼠标左键被释放,并且不会基于
MouseUp事件

在许多情况下,当我们需要它时,我们只需为
MouseUp事件编写一个事件处理程序

这很简单,而且很有效

但是,在某些情况下,使用
MouseUp事件将不会有用,
例如,当我们已经在另一个不同的事件处理程序中时,
调用此事件处理程序时,可能会按下鼠标左键,我们需要等待它被释放。 (目标是要有一个单一的代码流,而不必在几个可能已经被另一个代码占用的地方进行分割)

我是这样实施的:

public void WaitForMouseUp()
{
    while( (Control.MouseButtons&MouseButtons.Left)!=0 )
        Application.DoEvents();
}
它起作用了,
例如,当您在
控件的事件处理程序中时,可以使用它。输入
事件,
如果控件是通过鼠标输入的,则此功能将一直阻止,直到松开鼠标按钮

我只担心一件事:
我在那里使用的是
Application.DoEvents()
,我想知道是否有其他方法代替
Application.DoEvents()
。 (Application.DoEvents();有可能重入的缺点,因此,出于这个原因,我尽可能减少使用它)


任何人都知道我可以用什么来替代
应用程序.DoEvents()
部分?

如果您不想在单个方法中编写流,可以使用TaskCompletionSource创建一个等待已久的流程

您的流程:

await MouseUp();

...

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

public ... OnMouseUpEvent() {
  _tcs?.SetResult(true);
}
对不起,伪代码,我会在收到手机以外的东西后更新


OT:评论:跳出框框思考

如果不想在单个方法中编写流,可以使用TaskCompletionSource生成一个可等待的流

您的流程:

await MouseUp();

...

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

public ... OnMouseUpEvent() {
  _tcs?.SetResult(true);
}
对不起,伪代码,我会在收到手机以外的东西后更新

OT:评论:跳出框框思考

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

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

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


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

这里有一个很棒的方法来满足你的要求。使用微软的反应式扩展,让一行代码做你想做的一切

被动扩展提供了大量可应用于事件的操作符

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

        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);
现在订阅事件以便能够处理它:

        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()


只需获取“Rx WinForms”即可为您的项目获取信息。

这里有一个很棒的方法来满足您的要求。使用微软的反应式扩展,让一行代码做你想做的一切

被动扩展提供了大量可应用于事件的操作符

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

        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);
现在订阅事件以便能够处理它:

        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()


只需获取“Rx WinForms”就可以为您的项目获取比特。

事实上@Kai Brummund所建议的是我的to的一个变体。从那里调整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;
    }
}

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

事实上,@Kai Brummund所建议的是我的to的一种变体。从那里调整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;
    }
}

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

我需要一个小功能来等待鼠标左键释放。不,你真的不知道。你需要的是了解事件。显然不是这样。事件驱动的GUI需要异步、事件驱动的编程风格。强制采用同步方法的尝试是失败的。请使用其他线程进行处理。不管怎样,使用
Application.DoEvents()
您将移动到不同的事件,并且可能会打乱流程。这并不能解释什么。您不了解事件驱动编程。这就是这里显而易见的许多问题的根源。从一开始就很清楚。我需要一个小功能,等待鼠标左键释放。不,你真的不知道。你需要的是了解事件。显然不是这样。事件驱动的GUI需要异步、事件驱动的编程风格。强制采用同步方法的尝试是失败的。请使用其他线程进行处理。不管怎样,使用
Application.DoEvents()
您将移动到不同的事件,并且可能会打乱流程。这并不能解释什么。您不了解事件驱动编程。这就是这里显而易见的许多问题的根源。从一开始就很清楚。大卫,你在试图理解别人在做什么以及他需要什么时,太固执,太预防了。相信我,自2001年以来,我已经在.NET中使用了数千次MouseUp事件。在这个节目中,我想要一些不同的东西。设法压制