Best way to suspend control events in Windows Forms? - winforms

This seems like a very simple and a very common problem. The simplest example I can think of is this:
The form has five checkboxes with a "check all/check none" checkbox above them. When a user selects checking all checkboxes, I toggle the states of the "children" - obviously I don't want to fire the check events of all the children until I am done setting all of the checkboxes.
I can't find a form-wide suspend control event. If I'm simply missing it then great simple answer. Barring a simple solution that I am just missing, what is the best way (best practice? accepted solution?) to suspend form control events?

I've come across this before and usually seen people do this:
/*SNIP*/
private bool isMassUpdate;
public void Check1_Check(object sender, EventArgs e)
{
if(!isMassUpdate)
{
do some stuff
}
}
/*SNIP*/
You can also detach and reattach the event handlers, however, I'm told this can be a source of memory leaks.
Information on memory leaks and event handlers: They're not directly linked to attaching and detaching, but we've seen in one of our applications that bad referencing of event handlers down inheritance trees can cause it.
.NET Memory Leak Case Study: The Event Handlers That Made The Memory Baloon
On event handlers and memory leaks

What I do in these cases instead of having a boolean value that suspends events, I use a counter. When the count is > 0, then suspend events, when the count = 0, then resume events. This helps with the problem if I have multiple things that could request a suspension of events.
The other useful thing is if I need to suspend events in a block, I create a little helper class that is IDisposable that I can use in a "using" block (in C#) so I don't forget to decrement the counter once I'm out of scope.

From your other question, I'm going to guess you're using VB .NET. So, RemoveHandler is your best bet. Normally in VB people set up event handlers using the Handles clause. But you can also do it this way:
AddHandler chk1.CheckedChanged, AddressOf DoSomething
where DoSomething might look like this:
Private Sub DoSomething(ByVal sender As Object, ByVal e As EventArgs)
' whatever
End Sub
AddHandler wires up the event, so it'll fire. To get it not to fire, use RemoveHandler:
RemoveHandler chk1.CheckedChanged, AddressOf DoSomething
Before updating the Checked property of your child checkboxes, call RemoveHandler on each of them; then when you're done, call AddHandler to put the event handlers back. If all your checkboxes use the same handler, you can put them in a collection and loop through the collection to add or remove the handlers.

You could also consider handling 'click' events for the buttons, rather than check-changed. That might be nearer to your intent.

In my Visual Basic 6.0 application I had to handle users double-clicking everything, so on all my event handlers I have one line that checks a global variable
Private bSuspendEvents as Boolean
Private Sub Button1_Click()
On Error Goto ErrorHandler
If bSuspendEvents then Exit Sub
bSuspendEvents = True
'Do stuff
NormalExit:
bSuspendEvents = False
Exit Sub
ErrorHandler:
'Handle Error
Resume NormalExit
End Sub

See Stack Overflow question How do I ignore simple events firing when changing control states in C# Windows Forms?
For the more general case, if you have complex relations between the controls, you could check which control fired the event by checking its own Focused property... each object is only dependent on itself.

Related

Weird race condition by using ViewModel-first (binding) approach

I am experimenting with a simple ViewModel-first based WPF application and some primitive navigation logic. The application consists of two views (screens). One screen contains a button "Go forward" and the other a button "Go backward". By pressing one of the buttons a delegate command is invoked, which in turn causes the shell view-model to switch the active screen. Screen 1 switches to Screen 2, whereas Screen 2 switches to Screen 1.
The problem with this approach is, that it introduces a race condition. When clicking fast enough there is a chance that the corresponding action (go forward/go backward) is executed twice, thus causing the application to fail. The interesting thing about that is that the screen has already been changed, but the UI doesn't reflect the state changes instantaneously. Until now I never had experienced this kind of gap and I made this experiment just to prove, that a single-threaded (dispatched) WPF application is automatically thread-safe.
Does somebody have an explanation for this odd behavior? Is the WPF binding mechanism too slow, so that the button can be pressed a second time, until the UI has updated itself to represent the new screen state?
I have no idea how to fix this according to the recommendations for developing mvvm applications. There is no way to synchronize the code, because there is only one thread. I hope you can help me, because now I feel very unsecure with relying on the WPF data binding and templating system.
Zip archive containing the project files
MainWindow.xaml:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Screen1}">
<local:View1/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Screen2}">
<local:View2/>
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<local:ShellViewModel/>
</Window.DataContext>
<Grid>
<ContentControl Content="{Binding CurrentScreen}"/>
</Grid>
</Window>
The ShellViewModel containing a "go forward" and "go backward" method:
Public Class ShellViewModel
Inherits PropertyChangedBase
Private _currentScreen As Object
Public Property Screens As Stack(Of Object) = New Stack(Of Object)()
Public Sub New()
Me.Screens.Push(New Screen1(Me))
Me.GoForward()
End Sub
Property CurrentScreen As Object
Get
Return _currentScreen
End Get
Set(value)
_currentScreen = value
RaisePropertyChanged()
End Set
End Property
Public Sub GoBack()
Log("Going backward.")
If (Me.Screens.Count > 2) Then
Throw New InvalidOperationException("Race condition detected.")
End If
Log("Switching to Screen 1")
Me.Screens.Pop()
Me.CurrentScreen = Me.Screens.Peek()
End Sub
Public Sub GoForward()
Log("Going forward.")
If (Me.Screens.Count > 1) Then
Throw New InvalidOperationException("Race condition detected.")
End If
Log("Switching to Screen 2.")
Me.Screens.Push(New Screen2(Me))
Me.CurrentScreen = Me.Screens.Peek()
End Sub
End Class
The Screen class containing just a delegate command to start the action:
Public Class Screen1
Inherits PropertyChangedBase
Private _clickCommand As ICommand
Private _shellViewModel As ShellViewModel
Public Sub New(parent As ShellViewModel)
_shellViewModel = parent
End Sub
Public ReadOnly Property ClickCommand As ICommand
Get
If _clickCommand Is Nothing Then
_clickCommand = New RelayCommand(AddressOf ExecuteClick, AddressOf CanExecute)
End If
Return _clickCommand
End Get
End Property
Private Function CanExecute(arg As Object) As Boolean
Return True
End Function
Private Sub ExecuteClick(obj As Object)
Threading.Thread.SpinWait(100000000)
_shellViewModel.GoForward()
End Sub
End Class
There is no weird race condition
I've run your code. There is one thread. The main one.
One thread = no race condition.
Why do you want to prove the following ?
I made this experiment just to prove, that a single-threaded
(dispatched) WPF application is automatically thread-safe.
It's a bullet proof fact. One thread = thread safe (as long as you don't share data process wide, but then it's not thread safety anymore).
Binding and Method no supporting successive calls
In fact, your methods GoBack and GoForward are not supporting successive calls. They should be called one after another.
Thread safety here doesn't imply that your methods cannot be call twice in a row. If there is any task queue in the process, methods can be called twice.
What you may intend to prove is maybe the following:
Clicks are captured and processed in line, without any task queuing between
the click, the property changed event raised, the dispatcher
invocation, the binding / display refresh.
It's clearly Wrong!
When you call Dispatcher.BeginInvoke or Invoke, it's using internally a queue of tasks. And nothing prevent you from queuing twice the same task coming from two similar clicks for example.
To be frank, I was unable to reproduce your bug. I think it's the same thread that captures clicks that dispatch it to your code and then display it on screen. However since task for click events, display refresh are in the same queue, it is theoretically possible to enqueue two clicks before screen change. However :
I cannot click fast enough to beat my cpu.
I don't think the SpinWait are needed.
Something may miss in my configuration.
Why not making your methods supporting successive calls ?
GoBack and GoBackward could check a status and do nothing if the current status is not valid.
You could have used:
1. Two screens both instantiated from the start.
2. A bool to indicate the current state (Forward or Back).
3. An enum to be more clearer in code.
4. A state machine.. no! I'm kidding.
Note: Why using a Stack to push and pop screen(only one by the way) ? and...
in case, you add another thread:
Stack pop / push are not thread safe.
Instead use ConcurrentStack<T>
Simulation
Even when the UI thread is frozen or doing something, another inputs are being collected. Try this out (Sorry for C# but you get the point):
private void ButtonClick(object sender, EventArgs args)
{
Debug.WriteLine("start");
Thread.Sleep(6000);
Debug.WriteLine("End");
}
Click the button, then place a breakpoint on the Start line, click the button again before the UI thread unfreezes. and you will see that exactly 6 seconds after your first click the breakpoint gets hit.
Explanation
The UI thread can obviously only perform 1 action at a time, but it has to be optimized for multithreading - meaning it queues it's actions. Therefore any PropertyChanged (or any handler at all including the OnClick) is only queueing the action for the UI thread. It doesn't jump out of your code to update the UI elements in the middle of your setter. If you call Thread.Sleep after the setter you will see that you don't see any change - because the UI thread didn't get to invoke the update yet.
Why is this important
In your code you first Push a screen and then set is as current, calling propertyChanged. That does not change the screens immediately, it just queues it for the update. There is no guarantee that another click doesn't get scheduled before that.
You could also achieve freezing your UI thread by calling PropertyChanged a million times causing it to freeze during the update itself. Yet the clicks in the meantime would be collected.
So your "safepoint" - the place where it is safe that no other click can be scheduled now - is not after the Setter is finished, but after the Loaded Event is called on the new window.
How to fix
Read
Fab
's answer :)
Dont think that just because the UI thread is blocked at the moment nothing gets in. If you want to disable inputs while something is being calculated you do need to disable the inputs manually.
Possible solutions
Set IsEnabled, Visibility, IsHitTestVisible
Some Overlay or whatever
Boolean parameter, that could globally allow or disallow all methods coming (basically a lock)
I cannot reproduce the described behavior - double clicking causes the app to first "go backward", and then "go forward" at my side. Nevertheless, I think that expecting the button to disappear before the user can click it for the second time is not a good design (especially in case of devices that for instance have a separate "double-click" button), and I would personally never rely on that.
What I think is the best way to proceed in this situation is to properly implement the CanExecute method, so that it not only returns true (which by the way is most likely redundant), but rather queries the _shellViewModel whether it's in a state allowing to invoke the method called by ExecuteClick (in case of GoForward it should return true if there is only one element on the stack). I cannot test that (because I cannot reproduce the behavior in question), but I'm pretty sure that even if the user clicks the same button twice, the second call to CanExecute will occur after the first call to ExecuteClick, thus the model will be guaranteed to be "up-to-date" (the result will be false and GoForward will not be called again).
#Pavel Kalandra:
While it is possible for a simple click event-handler to be queued multiple times, even if the UI thread is blocked, I can't reproduce this behavior with a delegate command. Hence I assume that the WPF framework does handle the invocation of an command a little bit differently compared to a simple click event-handler. Moreover, in your example the click event is already queued before the execution of the event-handler has finished. In my situation this is not the case.
To prove this assumption I made a further experiment: By using a command that blocks the UI thread for a few seconds and then shows a message, you can see that it is not possible to invoke it multiple times during its invocation. I believe that the WPF framework is somehow preventing this from happening. Therefore both scenarios aren't comparable to one.
But I think that your explanation is still correct. Pushing the screen causes the PropertyChanged-event to be fired, but the screen is not updated immediately. Indeed the associated job is pushed onto the dispatcher queue and is scheduled. As a consequence there is a short time span during which it is possible to invoke the command a second time.
#Fab:
When you strongly rely on the accepted definition of a race condition, then there shouldn't be one in my sample application. But for simplicity purposes I would like to call it still a race condition because the scheduling of jobs makes the execution nondeterministic.
Nevertheless, the assumption I was intended to prove, is wrong. I wanted to prove it because of threading problems we are currently faced with. Our application simulates multiple modal methods at the same time, therefore it relies on multiple threads. Because the interactions, a user is allowed to, aren't properly synchronized there is a high chance for race conditions to happen (synchronizing them is not an option because of other reasons).
At the moment I am working on a prototype which doesn't use threads so heavily. My hope was, that by executing everything on the dispatcher thread race conditions (or similar problems) shouldn't be possible. At least for a ViewModel-first approach this seems to be wrong because of the way the WPF is scheduling binding updates.
I have used a simple scenario where it is easy to provide a fix to the potential "race condition". But in general it won't be easy to write a Bulletproof WPF application. A flag to indicate the direction (forward/backward) won't be enough when dealing with multiple screens. But the command delegate could check if it is invoked from the active screen.
PS: As long as I rely exclusively on the dispatcher thread to execute actions, I see no need for using a ConcurrentStack ;-)
I have come across another similar issue, that proves that UI scheduling can in fact introduce race conditions even if the application is single-threaded.
In the example a piece of code is called, that is supposed to be atomic. Because scheduling is used using different priorities, the code may be interrupted in the middle of execution.
This is an example that I found in a similar form in our production code. Users have mentioned an issue that is spontaneously occuring. I have found out then that a SelectionChanged-event was interrupting a piece of code that was supposed to be executed as a block.
public partial class MainWindow : Window
{
private bool inBetweenMethod;
public MainWindow()
{
InitializeComponent();
this.timer = new DispatcherTimer(DispatcherPriority.Loaded);
this.timer.Interval = TimeSpan.FromMilliseconds(10);
this.timer.Tick += Timer_Tick;
this.timer.Start();
this.MethodThatIsSupposedToBeAtomic();
}
private void Timer_Tick(object sender, EventArgs e)
{
if (inBetweenMethod)
{
throw new Exception("Method was interrupted in the middle of execution".);
}
}
private void MethodThatIsSupposedToBeAtomic()
{
inBetweenMethod = true;
Dispatcher.Invoke(new Action(() =>
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine("iterating");
}
}), DispatcherPriority.ContextIdle);
inBetweenMethod = false;
}
}

Allow a user to override a default action via an event (eg use of e.handled or e.cancel = true or false)

I have a WPF Custom control which I have been building as I happen to need a particular functionality and I want to learn how to do this properly. Most of the control has now come together nicely and is generally pretty usable. There is now one feature that I'd like to implement, however, I'm not sure if it's possible in the first place or if it is how I would or should go about implementing it.
So here goes by way of my explanation of what I have and what I would like. Feel free to ask for extra clarification if required.
My control raises a custom event which passes along some custom event args. The event is declared like so in the control.
Public Event RecordControlButtonClicked(sender As Object, e As VtlDataNavigatorEventArgs)
In most cases when this event is raised the end user has simply clicked the button, nothing other than raising the event will take place because the developer using the control will most likely want to decide how they handle the situation at that particular moment and the custom event args provide more than enough info to do that.
There are however a couple of buttons where in all honesty the developer will probably want to happen what I (as the developer of the control) envisage should happen most of the time but on the odd occasion might not and therefore needs the opportunity to cancel out of it. So what I'm wondering is how I might achieve the following:
The end user clicks a button
The control button raises my custom event
The developer using my custom controls decides they wish to ignore what I (the creator of the control) thinks should happen, so they do something like e.handled = true in the code where they handle my custom event.
Somehow that message gets back to the button that raised the event, it seems that it's been 'handled' or 'canceled' and as a result does nothing
My question is how do I create that sort of functionality and is the event that I currently have declared the correct sort of event to handle this?
Well just in case anyone stumbles across this looking for an answer.
It transpires that my basic mistake lay in the way that I'd Created my custom events class. Originally I'd followed several examples that I'd read about and had my custom events inherit from EventArgs;
Public Class MyCustomEventArgs
Inherits EventArgs
End Class
What I should have done was have it inherit from CancelEventArgs;
Public Class MyCustomEventArgs
Inherits System.ComponentModel.CancelEventArgs
End Class
That provides the Property that I was after. Now when the event is actually handled upstreap the developer can add an e.cancel = true and all I need to do back in the originating call to raise the event is listen out for e.cancel ;
RaiseEvent MyCustomEvent(sender, e)
if e.cancel = true
'presumably don't do something
Else
'do what needs to be done
End If

Cross thread calls vs multiple controls

I have a Winform GUI that have an UDPClient listener thread working on second plane to avoid halt the GUI; when the thread receive something call a Sub in the WinForm code; that Sub process the data and must to fill different TextBox depending on the received data.
As you know if I try to change the text property of any control from the Sub I will receive a Cross Thread error.
So in order to avoid that I make some delegates for a few control(just a test) and works OK.
But the WinForm have more than 100 controls and I was wondering if there's some way to do it with less code.
Making a search I found this two questions.
Multi-threaded WPF Application: Dispatcher Invoke. A more efficient way?
Change WPF controls from a non-main thread using Dispatcher.Invoke
On the first link they talk about use Dispacher, something like
Public Shared Sub UiInvoke(a As Action)
Application.Current.Dispatcher.Invoke(a)
End Sub
I wrote that code in my Form but the IDE(VS2010/NET4.0) says that "Current" is not a member.
I think I'm missing something, I never used before or do something similar as Dispacher, I usually use Delegates.
What I'm doing wrong? There's another way to control many controls with one Delegate?
I just need to read or write the text property.
You can use anonymous Subs:
Me.Invoke(
Sub()
' Update controls here
End Sub)

Is it possible to have CommandManager requery only specific WPF command instead of all?

I'm trying to implement a highly responsive UI for my MVVM application, so I've chosen to have all command handlers automatically execute in a BackgroundWorker so they don't block the UI.
But at the same time, I don't want the user to be able to execute the same command while it is still executing in the background. The solution seems obvious:
When Executed handler is invoked, have the CanExecute handler return false
Start the BackgroundWorker async
When BackgroundWorker finishes, have the CanExecute handler return true again.
Problem is, I need to notify WPF after step 1 and again after step 3 that CanExecute has changed and that it should be required. I know I can do it by calling CommandManager.InvalidateRequerySuggested, but that causes CanExecute handlers of all other commands to be requeried as well. Having a lot of commands, that's not good.
Is there a way to ask for requery of a specific command - i.e. the one that is currently being executed?
TIA
I think you only need to raise the ICommand.CanExecuteChanged event for these two cases, and the corresponding control should update its IsEnabled state.
gehho is right, just raise the CanExecuteChanged event. But honestly, I don't know your implementation, but if your CanExecute predicate is simple, I think it will be hard to notice that wpf is requerying. I can't believe you will notice a performance hit. Have you tested performance? I mean let's say you have 20 ICommands in a ViewModel(which is probably too much) if the ICommand.CanExecute points to a boolean field that you are setting to true and false based on the backgroundworker it should take less than nanoseconds. But if the CanExecute is pointed to a method that evaluates, then it may be longer, but I still doubt it would be noticeable.
But hey, I don't know your implementation.
Use MVVM Helper's `DelegatingCommand' which exposes RaiseCanExecuteChanged() for the Command.
So you can subscribe to RunWorkercompleted event on BackgroundWorker and depending on the kind of work logged (maybe you can return as part of the Result) you call Command.RaiseCanExecuteChanged() from ViewModel.

AccessViolation exception when form with AxWindowsMediaPlayer closed

I have a AxWMPLib.AxWindowsMediaPlayer on a form. When I close the form, I get "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." exception. It is OK with hiding the form but not with closing. Everything's fine when the component is removed from the form.
This is Winforms .Net3.5.
Any help appreciated.
This was happening to me, and it was when closing the form during a key press.
Seems the WMP control will cause problems if it has a key event to process.
Example with Form.KeyPreview = True
Sub Form_KeyDown(e As KeyEventArgs)
AxWindowsMediaPlayer1.Dispose()
End Sub
Causes an Access Violation.
Sub Form_KeyDown(e As KeyEventArgs)
e.Handled = True
AxWindowsMediaPlayer1.Dispose()
End Sub
Closes cleanly, as the key press is blocked from reaching the WMP control.
Same thing happens when the form is closed as will dispose of the control.
Sometimes when working with ActiveX objects in .NET applications it is necessary to force garbage collection on exit. I generally do this in Form_Closing using:
GC.WaitForPendingFinalizers()
GC.Collect()
Also, if you have setup any event handlers for the object, you will want to disconnect them explicitly. I've found on a number of occasions that ActiveX objects will still remain active in the garbage bin and will attempt to call the event handler even after they have been disposed.
It may also be worth it to make sure playback has stopped before you try to dispose of the object.
ActiveX objects may have some sensitive dependencies on being closed in the correct order when the parent form is closed, otherwise they may go on living until gc runs - try looking through the interface for the control for any methods that look like they may have to do with closing, or destroying the object and calling those.
I think I have it:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
axWindowsMediaPlayer1.close();
}
simply found the method on the doc http://msdn.microsoft.com/en-us/library/windows/desktop/dd562388(v=vs.85).aspx
I thought I'd give it a go. it seems much better now.

Resources