A Better Way to Implement a WaitForMouseUp() Function? - c#

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?

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.

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.

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!

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.

Related

Execute an event only if it wasn't fired for a given amount of time in C#/WPF [duplicate]

This question already has answers here:
WPF: Slider with an event that triggers after a user drags
(12 answers)
Closed 2 years ago.
I have a WPF app with a MouseWheel event. The operations in this event is quite heavy. So, I would like to execute this event only when the user has stopped scrolling (i.e.: if he doesn't scroll for a given amount of time).
In JS, this is quite easy, I can just put the setTimout in a var and then do a clearTimeout on that var if another scroll happened before the execution of that setTimeout (this is quite useful for auto-completion for instance).
How can I achieve that in c#?
This is quite easy using Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive.Windows.Threading (for WPF) and add using System.Reactive.Linq; - then you can do this:
IObservable<EventPattern<MouseWheelEventArgs>> query =
Observable
.FromEventPattern<MouseWheelEventHandler, MouseWheelEventArgs>(
h => ui.MouseWheel += h, h => ui.MouseWheel -= h)
.Throttle(TimeSpan.FromMilliseconds(250.0))
.ObserveOnDispatcher();
IDisposable subscription =
query
.Subscribe(x =>
{
/* run expensive code */
});
The docs say this about Throttle:
Ignores the values from an observable sequence which are followed by another value before due time with the specified source and dueTime.
Something like the following might suit your needs
public class perRxTickBuffer<T>
{
private readonly Subject<T> _innerSubject = new Subject<T>();
public perRxTickBuffer(TimeSpan? interval = null)
{
if (interval == null)
{
interval = TimeSpan.FromSeconds(1);
}
Output = _innerSubject.Sample(interval.Value);
}
public void Tick(T item)
{
_innerSubject.OnNext(item);
}
public IObservable<T> Output { get; }
}
Create an instance where T is the event args type for your event.
Set an appropriate timespan value - perhaps 1/4 second for your case.
Just call Tick() from you event handler, and then subscribe to the Output observable for a regulated flow of 'events'.

C# Events / Block when hit GUI

I'm working on a custom GUI with SharpDX.
I have user Input from a Form Object and assign Action Methods to the specific events. Below my UI I have a "drawing canvas" and I use Tool Objects that also listen to those Form Events.
But I'm a bit stuck on the matter of how to design my program to only pass those events to a second layer (in this case my canvas) when the first layer did not "hit" anything. In short: Only call "Tool.OnMouseDown" when "Button.OnMouseDown" did return false? Would a Chain Of Responsibility be the/a correct or possible approach?
Or shall I make the current Tool check if "Excecute (Vector2)" is above some gui element but I think this would lead to the kind of coupling I want to prevent.
Hope someone is willing to help/hint (sorry for no code examples, if it's to confusingly descriped please tell me ;))
Thanks!
(Disclaimer: I know I don't have to reinvent the wheel, but I use it partly to learn and improve on my design patterns and coding skills)
thanks to sharp-ninja's answer i did the following:
ok working with it like this now :) thanks again Mister Ninja
using System.Windows.Forms;
public class HandleMouseEventArgs : MouseEventArgs
{
public bool handled { get; protected set; }
public HandleMouseEventArgs(MouseEventArgs args) : base(args.Button, args.Clicks, args.X, args.Y, args.Delta)
{
handled = false;
}
public void SetHandled()
{
handled = true;
}
}
Fortunately in .Net events get called in the order in which they are registered. You can use a handlable event arg so that the first handler of the event can tell subsequent event handlers whether the event was handled.
event EventHandler<MyHandlableEventArg> MultiLevelEvent;
Then in your main program:
// First event handler
MultiLevelEvent += (s, e) => { if(x) e.Handled = true; };
// Subsequent event handler
MultiLevelEvent += (s, e) => { if(!e.Handled) { /* Do Work */ } };

How to remove all event handlers in Windows App?

Here is the situation:
public async void Test()
{
List<string> fileTypeFilter = new List<string>();
fileTypeFilter.Add(".jpg");
fileTypeFilter.Add(".png");
var folder = KnownFolders.PicturesLibrary;
var queryOptions = new QueryOptions(CommonFileQuery.OrderByName, fileTypeFilter);
var queryResults = folder.CreateFileQueryWithOptions(queryOptions);
//queryResults.ContentsChanged += null;
queryResults.ContentsChanged += QueryResults_ContentsChanged;
}
I call Test many times, so when some changes happened in that folder, QueryResults_ContentsChanged fires for manytimes, but I just want only once. I tried "+= null", but it does not work, so I have no idea how to remove all event handlers from the local variable queryResults.
I don't see any use case that requires calling your Test method many times, I suggest verify/validate your design.
What I understood from your question is, you want to check whether QueryResults_ContentsChanged is attached to any other event or not, it is not possible (unless you've your own logic).
In general, an event can be unsubscribed as below.
queryResults.ContentsChanged -= QueryResults_ContentsChanged;
Hope this helps.
You can use -= operator to Unsubscribe event.
queryResults.ContentsChanged -= QueryResults_ContentsChanged;
See MSDN for more detail that how to Subscribe and Unsubscribe events

Throttle an Event Handler

In my WPF application, I have an event handler that gets called on the MouseEnter event of my UI element:
myUiElement.MouseEnter += myEventHandler
I would like to throttle myEventHandler so it doesn't get called more than once every second. How can I do this? Is Rx the best approach just for this? I'm using .NET 4.0 if it makes a difference.
Also, I need to make sure that the MouseLeave event always gets called before the next MouseEnter event; do I need to manage this on my own? Or is the framework already designed so that MouseLeave events will always be called before the next MouseEnter event? What if I have asynchronous code in these event handlers?
Using Rx, you want to use the Sample method or Throttle.
Something like this should work (untested):
Observable
.FromEventPattern<TextChangedEventArgs>(myUiElement, "MouseEnter")
.Sample(TimeSpan.FromSeconds(1))
.Subscribe(x => ... Do Stuff Here ...);
The difference between Sample and Throttle is that Sample will take a value every 1 second no matter when the last value was taken, whereas Throttle will take a value and then wait another 1 second before taking another.
It probably depends on what you are shooting for...
You could use reactive extensions, but you could accomplish this just as easily with a timer.
Set a flag along with a Timer. When the timer tick event fires, set the flag to false, disable the timer, and run the code for your event. Then, in your control event handlers, have the handler code skipped if the flag is set.
bool flag;
DispatcherTimer timer;
public constructor()
{
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += (s,e) => {
flag = false;
timer.Stop()
DoThrottledEvent();
}
}
void mouse_enter(object sender, MouseEventArgs args)
{
if(!flag)
{
flag = true;
timer.Start();
}
}
void DoThrottledEvent()
{
//code for event here
}
Reactive extensions introduces an extra dependency, but they are a bit of fun. If you are interested, go for it!
Another approach would be to use a private field to keep track of the "time" when the last mouse event occurred, and only continue processing if that time was more than one second ago.
DateTime _lastMouseEventTime = DateTime.UtcNow;
void OnMouseEnter(object sender, MouseEventArgs e)
{
DateTime now = DateTime.UtcNow;
if (now.Subtract(_lastMouseEventTime).TotalSeconds >= 1)
{
// do stuff...
}
_lastMouseEventTime = now;
}
This ensures that "stuff" gets done at least one second apart, which is what I think you were asking for.

C# loop while mousedown button pressed

I'd like to use loop while left mousebutton is pressed:
private void Loop_MouseDown(object sender, MouseEventArgs e)
{
while (e.Button==MouseButtons.Left)
{
//Loop
}
}
I can't use solution from this thread:
C# how to loop while mouse button is held down
because I'm sending via RS232 data and using timer with it's own interval doesn't work. Also any solution from this topic doesn't work for me.
It can't also work one like here:
if (e.Button == MouseButtons.Left)
{
//loop
}
This solution also doesn't work:
bool isLooping = false;
//on mouse down
private void myControl_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) {
isLooping = true;
runLoop();
}
//on mouse up event
private void myControl_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) {
isLooping = false;
}
//This is the main loop you care about. Put this in your application
//This should go in its own thread
void runLoop() {
while (isLooping) {
//do stuff
}
}
because calling runLoop would block the thread, and so the MouseUp event would never fire.
So how to make it work correctly?
Use a BackGroundWorker. Perfect for your problem.
Put the loop function in the worker and start / stop the worker on mouse events.
If using a timer won't work, you'll need to send the data on a different thread, and signal that thread from the MouseUp handler.
The correct way to do this would be to put the rs-232 send function into a separate thread so the UI will remain responsive, then you can start and stop it when the mouse events change.
This page might be useful:
http://www.yoda.arachsys.com/csharp/threads/winforms.shtml
These scenarios are very complicated to implement - see your handlers and boolean variables for storing the state.
I would suggest to use Reactive Extensions.
Edit:
It will probably be slightly over-engineered (I don't know if this is the only scenario Elfoc wants to implement). In Rx you can create observable sequence of events
var mouseDown = Observable.FromEvent<MouseButtonEventArgs>(source, "MouseDown");
var mouseUp = Observable.FromEvent<MouseButtonEventArgs>(image, "MouseUp");
var mouseMove = from evt in Observable.FromEvent<MouseEventArgs>(image, "MouseMove")
select evt.EventArgs.GetPosition(this);
use LINQ-to-Rx to query and filter the events
var leftMouseDown = from evt in mouseDown
where evt.LeftButton == MouseButtonState.Pressed
select evt;
and compose it using Rx operators - until any mouse up event is raised take all the positions while left mouse is down
var q = from position in leftMouseDown
from pos in mouseMove.Until(mouseUp)
select new { X = pos.X - imageOffset.X, Y = pos.Y - imageOffset.Y };
Finally, subscribe to the observable sequence of positions and do your stuff
q.Subsribe(value => { ... });
Slightly modified from the code here.

Categories