0

I needed a small function that will wait for the left mous button to be released, and will not be based on the MouseUp event.

In many cases when we need this, we simply write an event handler for the MouseUp event.
It's simple, and it works.

There are however cases, where using the MouseUp event will not be useful,
such as when we are already in another (different) event handler,
and the left mouse button might be pressed when this event handler is called, and we need to wait for it to be released. (the goal is to have a single flow of code, and not have to split it between several places which might already be occupied with another code)

I implemented it this way:

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

It works,
you can use it for example when you are in the event handler for the Control.Enter event,
and if the control was entered via the mouse, then this function will block until the mouse button is released.

I only worry about one thing:
I am using Application.DoEvents() there, and I wonder if there another way instead of using Application.DoEvents(). (Application.DoEvents(); has disadvantages of possible reentrancy, and so, so for this reason I try to minimize using it, whenever possible)

Anyone has an idea with what I can substitute the Application.DoEvents() part?

spaceman
  • 1,061
  • 1
  • 11
  • 31
  • can't you use [mouseup event](https://msdn.microsoft.com/en-us/library/system.windows.forms.control.mouseup(v=vs.110).aspx) ? – bansi Jan 05 '16 at 05:36
  • Often yes, but there are times where you need to have a block of code doing something, and not have it on several different event handlers (which some might already do another work) – spaceman Jan 05 '16 at 05:38
  • 3
    *I needed a small function that will wait for the mouse's left button to be released.* No. You really don't. What you need is to understand events. – David Heffernan Jan 05 '16 at 05:38
  • I understand and use event very well. – spaceman Jan 05 '16 at 05:39
  • See this: http://stackoverflow.com/a/6749310/1004522. – Ebad Masood Jan 05 '16 at 05:39
  • 1
    Well, apparently that's not the case. Event driven GUIs require asynchronous, event driven programming style. Your attempt to force a synchronous approach is your downfall. – David Heffernan Jan 05 '16 at 05:40
  • 1
    use a different thread for processing. anyhow with `Application.DoEvents()` you are moving to different events and probably mess up the process. – bansi Jan 05 '16 at 05:41
  • David, please read the Note I added to the original question, hopefully it will clarify why this time the `MouseUp event` was not relevant. – spaceman Jan 05 '16 at 05:42
  • 3
    That explains nothing. You don't understand event driven programming. That's the source of the many problems that are evident here. – David Heffernan Jan 05 '16 at 05:44
  • I edited again. See if it's clearer now.. – spaceman Jan 05 '16 at 05:51
  • 1
    It was clear right from the start. – David Heffernan Jan 05 '16 at 06:00
  • 1
    Can I ask for a scenario that you need to wait for a mouse up when you're in another event? Like what? A mouse down? Or is there some other scenario that you are thinking of? A want to understand what you're trying to do here in terms of the overall problem. – Enigmativity Jan 05 '16 at 06:50
  • Hi Enigmativity. Sure. The `Control.Enter event` for example. A control can be entered via the Keyboard(like using [Tab]), or via the Mouse(like when being clicked). If the control was entered in the second way (mouse click), and you want to do some operation that cannot happen while the mouse is still down, then you need to wait for it to be released.. – spaceman Jan 05 '16 at 06:58
  • I think it is quite clear what is being ask for here. – Enigmativity Jan 05 '16 at 12:45

4 Answers4

2

Here's an awesome way to do what you're asking. Use Microsoft's Reactive Extensions to make a single line of code do everything you want.

The reactive extensions provide a whole lot of operators that can be applied to events.

So first some basic observables that directly relate to normal control events:

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

Now we need a query that will fire only once when the mouse up occurs, but only if the mouse has entered the button1, but only before it leaves.

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

Now to subscribe to the event to be able to handle it:

        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`
                    */
                });

It now because very simple to unsubscribe as the type of subscription is IDisposable. So you simply call subscription.Dispose();.

Just NuGet "Rx-WinForms" to get the bits for your project.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • Hi Enigmativity. Thank you very much for the detailed answer. It looks very nice, however alot of code complexity relatively to what I need.. (in such a case, I would probably prefer to split my code between 2 event handlers: `Control.Enter` and `Control.MouseUp`) – spaceman Jan 05 '16 at 07:46
  • 2
    @spaceman - I appreciate your comment, but if you are saying that this is a lot of complexity then I'm somewhat inclined to agree with David Heffernan that you probably need have a slightly deeper understanding of events to get what you need. I'm not trying to be rude, just a little frank. I hope you don't take it wrongly. – Enigmativity Jan 05 '16 at 09:15
2

In fact what @Kai Brummund is suggesting is a variation of my answer to Force loop to wait for an event. Adjusting the code from there for MouseUp is simple as

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;
    }
}

and the usage is

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

The same technique can be used for any event.

Community
  • 1
  • 1
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
1

If You wan't to write a flow within a single method, you can make an awaitable using a TaskCompletionSource.

Your flow:

await MouseUp();

...

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

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

Sorry for Pseudo code, will update this once I get something other than a mobile.

OT: Commenters: Think outside of the Box!

Kai Brummund
  • 3,538
  • 3
  • 23
  • 33
  • Think outside the box? What do you mean? – David Heffernan Jan 05 '16 at 06:00
  • TaskCompletionSource was created to enable scenarios where you need (or just want) to bring execution flows into an async/await flow, instead of splitting I up into loads of event handlers. This vastly improves readability of a method flow. Although this probably wasn't intended for UI events, it is still a valid method. Just because something was event based when it was created, doesn't mean we have to keep it like that when there are ways that better suit our current case. – Kai Brummund Jan 05 '16 at 06:08
  • Thank you Kai. I like this idea and will use it. It also nice that someone understands what you need, unlike another person here... :) – spaceman Jan 05 '16 at 06:10
  • @spaceman Kai is saying exactly the same as me, you just don't understand it yet – David Heffernan Jan 05 '16 at 06:12
  • David, unlike you, Kai is giving a solution, and not saying "you don't understand X" – spaceman Jan 05 '16 at 06:17
  • I gave you a solution, the same one as it happens. To respond to mouse events in an asynchronous way. Anyway, it's good that you have learnt that an async approach is essential. – David Heffernan Jan 05 '16 at 06:22
0

I needed a small function that will wait for the mouse's left button to be released.

No you don't. WinForms GUI programming is event driven, asynchronous. You should use the MouseUp event to detect the mouse button's release. This does mean that you need to implement your logic using state based asynchronous techniques, rather than the synchronous model that you crave.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • David you are too stubborn and way too preventive in trying to understand what the other person is doing, and what he needs. Believe me, I have used the MouseUp event in .NET thousands of time, since 2001. In this program, I want something differently. Try to understand why, before you tell people "no, you don't understand events". – spaceman Jan 05 '16 at 05:57
  • You don't understand the event driven model. Which is why you find yourself calling DoEvents even though you know it is wrong. – David Heffernan Jan 05 '16 at 05:59
  • The DoEvents calling is temporary. That's why I created this question, to find out other ways to achieve it. Kai answered one. You on the other way enjoy to argue.. – spaceman Jan 05 '16 at 06:00
  • Kai is suggesting that you use the MouseUp event in an asynchronous event driven way, just like me. On the other hand, your chosen synchronous design leads inexorably to re-entrant message queue pumping of DoEvents. – David Heffernan Jan 05 '16 at 06:01
  • At least Kai is practical, and giving solutions, rather than arguing and telling people what they don't understand (leaving them empty handed with no progress) – spaceman Jan 05 '16 at 06:02
  • It seems to me that you don't want to learn. You decided ahead of time that the solution was synchronous. Why? – David Heffernan Jan 05 '16 at 06:03
  • I did not decide anything. I just gave a temporary solution, that works, but that I memtioned that I wish to change.. (because DoEvents() can be problematic) I would appreciate and learn much more, if you could elaborate on the `using state based asynchronous techniques, rather than the synchronous model that you crave` part. You would be much more helpful rather than arguing.. – spaceman Jan 05 '16 at 06:06
  • What more is there to say? Use the MouseUp event. I'm out of patience. Do it your way. You tell me not to argue, so I won't. – David Heffernan Jan 05 '16 at 06:08
  • 2
    @DavidHeffernan [This](http://meta.stackexchange.com/q/272389/245360) might be interesting to you. – Patrick Hofman Jan 05 '16 at 09:06