Is there some obscure way that I might have globally (accidentally) disabled all Touch events in my entire WPF application all at once -- so that they get "promoted" to mouse events? Is there some global WPF flag somewhere that turns them off. If so can anyone give me an idea of what it might be?
Detailed Explanation:
I've got a WPF desktop application (.NET Core 3.1) running on an MS Surface. For almost 2 years it's been working fine with both touch and Mouse handlers; If the user clicked with the mouse, the mouse handler got invoked. If the user touched with their finger, the ManipulationEvent (or TouchEvent) handler got invoked.
But recently I was seeing strange behavior and I finally realized that every single touch manipulation event was getting "promoted" to mouse a event before it ever reached any of my touch/manipulation handlers.
For example, here is a style I apply to Path objects in one of my views:
<Style x:Key="ShapeDragPathStyle" TargetType="{x:Type Path}">
<Setter Property="Cursor" Value="Hand" />
<EventSetter Event="MouseLeftButtonDown" Handler="Shape_OnLeftButtonDown" />
<EventSetter Event="MouseMove" Handler="Shape_OnMouseMove" />
<EventSetter Event="MouseLeftButtonUp" Handler="Shape_OnMouseLeftButtonUp" />
<EventSetter Event="TouchDown" Handler="Shape_OnTouchDown" />
</Style>
Now, if I put my finger down and drag a path that has this style, the OnLeftButtonDown handler will be called. The TouchDown handler never will.
To be complete, here is a list of all the touch events for which I have declared handlers in various places in my XAML and which I have been using for a long time.
ManipulationStarting
ManipulationStarted
ManipulationDelta
ManipulationCompleted
TouchUp
TouchDown
PreviewTouchDown
Not a single handler of these ever gets invoked anymore. I literally went and put break points on every handler of them, it doesn't matter where they are. It doesn't matter if I have IsManipulationEnabled="True" on them or not. The mouse-equivalent always hits invoked instead.
Nor does this appear to be something in Windows Settings. I've verified that it happens on multiple MS Surface machines and that it only appears to be happening in my application; I created a simple .NET Core 3.1 test app and added a ManipulationStarting handler and that worked fine.
Any ideas what I should look for?
(I'm sure I'm going to feel stupid when I find out but I feel that way already...)
Answering my own question because I have guessed at the cause by comparing touch-handling call stacks of the test app vs my app. The problem seems obscure but I can see someone else having this problem so here's what happened:
In short, the culprit was System.Management.ManagementEventWatcher
I wanted my app to be notified when a USB device was plugged in. So I created a ManagementEventWatcher that watched for such events
Watcher = new ManagementEventWatcher(
new ManagementScope("root\\CIMV2"),
new WqlEventQuery()
{
EventClassName = "Win32_DeviceChangeEvent",
Condition = "EventType = 2 OR EventType = 3",
});
Watcher.EventArrived += (sender, args) => { DeviceChanged?.Invoke(this, true); };
Watcher.Start(); // THIS WAS THE PROBLEM
Calling Start() on the main UI thread appears to change how the main thread handles window messages. The entire callstack looks different when I do this. Incoming Touch messages go right to mouse handlers before anything even reaches my code.
When I comment out the call to Start(), all the touch handlers are invoked again.
So I tried putting the call to Start() on a background thread:
Task.Run(() => Watcher.Start());
And now everything works fine. I'm not sure that's the final answer - I might even want to create my own dedicated thread to avoid messing up any thread pool threads - but clearly that's it.
So it would seem that ManagementEventWatcher is something best left off of UI threads, at least in WPF
Related
From a production application, we notice that our WPF buttons fire the ICommand.Execute method twice on fast double click.
Now, on every Command, the application is covered with a full-screen spinner animation, preventing any further interaction with the application.
This github repo contains a minimalistic repro of the issue. Note that:
when the Button's Command fires, the "IsBusy" flag is set to true
as a consequence, the BusyIndicator overlay will be shown
as a consequence, the Button cannot be pressed again until after 300ms
However, especially on slow computers, when fast double-clicking (really fast, like gaming fast that is), it is possible to fire the command twice without the BusyIndicator blocking the second call (this can be seen if the output shows 2 'click' lines right after one another).
This is unexpected behavior to me, as the IsBusy flag is set to true right away on the UI thread.
How come a second click is able to pass through?
I would expect the IsBusy Binding to show the overlay on the UI thread, blocking any further interaction?
The github sample also contains 2 workarounds:
using the ICommand.CanExecute to block the Execute handler
using the PreviewMouseDown to prevent double clicks
I'm trying to understand what the issue is.
What work-around would you prefer?
Diagnosis
This is only my guess and not a solid and confirmed info, but it seems that when you click the mouse button, the hit-testing is done immediately, but all the mouse related events are only scheduled to be raised (using the Dispatcher I presume). The important thing is that the control that is clicked is determined at the time the click occurred, and not after the previous click has been completely handled (including all UI changes that potentially follow).
So in your case, even if the first click results in showing the BusyIndicator covering (and thus blocking) the Button, if you manage to click for the second time before the BusyIndicator is actually shown (and that does not happen immediately), the click event on the Button will be scheduled to be raised (which will happen after the BusyIndicator is shown), causing the command to be executed again even though at that point the BusyIndicator will possibly be blocking the Button.
Solution
If your goal is to prevent command execution while the previous one is still executing the obvious choice is to make the Command.CanExecute result depend on the state of the IsBusy flag. Moreover, I wouldn't even call it a workaround, but a proper design.
What you're facing here is a clear-cut example of why you shouldn't make your business logic rely on UI. Firstly, because rendering strongly depends on the machine's processing power, and secondly because covering a button with another control by far does not guarantee the button cannot be "clicked" (using for example UI Automation framework).
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;
}
}
I have a rectangle which should trigger an event on TouchEnter.
But when I Touch the rectangle nothing happens until the longtouch is over. It looks like it is waiting to be sure there is no longtouch before the TouchEnter kicks in.
If I touch the rectangle and move slightly (so there can't be a LongTouch anymore) it triggers the event.
How can I get rid of this:
"I better wait for the longTouch to be finished until I trigger the touchEnter" ?
The same happens with the TouchDown event...
Set as high as possible in the visual tree the attached properties Stylus.IsPressAndHoldEnabled and Stylus.IsFlicksEnabled to false in xaml or in code.
These will disable most of the delay experienced when doing a touch enter and down due to the touch needing the delay to figure out what the user wants to do.
It happened only on my Touch-Monitor.
Using a Tablet it worked fine.
So I think it depends on the Hardware.
Just documenting this as a question an answer so that somebody else doesn't have to suffer the same pain.
I have a WPF application that animates pages, much like swiping on an iPhone. All was good until one of the pages needed to contain a WebBrowser. It did not respond at all well to the animation - when it was supposed to slide in, it wouldn't appear until you focused it, and when it was supposed to slide out, it would go away until you moved the mouse over it. In both cases it just popped in/out rather than animating.
Complicating matters, during the project it was decided to move back to .net 3.5 instead of 4 for unrelated reasons.
So the question is: how can I either (a) get the WebBrowser to properly animate; or (b) how can I hide the WebBrowser at the start of animation and show it again at the end. The animation is currently defined in XAML, and I don't particularly want to change it to code.
And a follow up question is: is there a better way, still using .net 3.5?
UPDATE The WPF WebBrowser is so pathetically lame compared to the WinForms one, I have swapped over, using WindowsFormsHost. Everything below still applies, but the WebBrowser is now not so nobbled (eg. it has a DocumentCompleted event).
I pretty quickly gave up on the option to animate the WebBrowser, as it just got all too hard, and instead decided to hide and re-show it. The start of the animation is triggered by a Command on the View Model. It then finds the page that should be displayed, creates it, and kicks off the animation through an attached property that reflects the transition state.
I created an interface, IRequireTransitionInfo, such that a call to IRequireTransitionInfo.TransitioningFrom gives it a chance to hide itself and IRequireTransitionInfo.TransitioningTo to show again. TransitioningFrom was easy, but TransitioningTo had to be called when the storyboard completed.
Initially, in the constructor of the View Model, it went looking for the Storyboard and hooked into its Completed event, as in the code below:
Storyboard animation = Application.Current.FindResource("SlideAnimation") as Storyboard;
if (animation != null)
{
animation.Completed += new EventHandler(animation_Completed);
}
And then the event handler:
void animation_Completed(object sender, EventArgs e)
{
IRequireTransitionInfo info = currentViewModel as IRequireTransitionInfo;
if (info != null)
info.TransitioningTo(currentView);
}
This seemed to be working pretty well with .net 4. After downgrading to .net 3.5, when the code above to hook up the Completed event ran, I got the following error:
Specified value of type 'System.Windows.Media.Animation.Storyboard' must have IsFrozen set to false to modify.
Despite some of the other answers on SO, you cannot unfreeze a frozen Freezable, and moving the code into the constructor of the MainWindow didn't help.
I went down the path of an attached property on the Storyboard that was bound to a command on the View Model.
<Storyboard x:Key="SlideAnimation" local:EventCommand.StoryboardCompleted="{Binding Source={StaticResource Locator}, Path=Current.MainViewModel.StoryboardCompletedCommand}">
However, this resulted in the following error at runtime:
Cannot convert the value in attribute 'ContentTemplate' to object of type 'System.Windows.DataTemplate'. Cannot freeze this Storyboard timeline tree for use across threads.
It seems you can't do any databinding on a Storyboard (under .net 3.5 at least). Consequently, I solved the problem somewhat inelegantly by having the attached property just define the string name of a resource that was expected to implement an interface supporting notification of storyboard completion.
<Storyboard x:Key="SlideAnimation" local:EventCommand.StoryboardCompletedHandler="Locator">
If anybody knows of a better way to handle this situation under .net 3.5, I would be glad to hear.
I found some of my winform application controls, such as DataGridView and ToolStrips, are referred to by UserPreferenceChangedEventHandlers. I have no idea what setting of the controls will generate such references and why such references keep my control alive in memory. How can I remove such references from that event? Thanks.
It is the delegate type for the SystemEvents.UserPreferenceChanged event. This event fires when Windows broadcasts the WM_SETTINGCHANGE message. Which typically happens when the user uses a control panel applet and changes a system setting.
Several controls register an event handler for this event, DataGridView, DateTimePicker, MonthCalendar, ProgressBar, PropertyGrid, RichTextBox, ToolStrip, NumericUpDown. They typically are interested in font or cue changes and anything that would affect the layout.
SystemEvents.UserPreferenceChanged is a static event. Registering a handler and forgetting to unregister it causes a memory leak, it prevents the control from being garbage collected. The listed controls ensure this doesn't happen, they unregister the event handler in either the OnHandleDestroyed() or the Dispose() method.
You'll get in trouble when neither of those two methods run. That will happen when you remove the control from the container's Controls collection and forget to Dispose() it. While forgetting to call Dispose() is not normally a problem, it is a hard requirement for controls. It is easy to forget too, controls are normally automatically disposed by the Form. But that only happens for controls in the Controls collection.
Also be sure to call Dispose() on forms that you display with the ShowDialog() method, after you obtained the dialog results. The using statement is the best way to handle that.
One more excruciating detail is important about the UserPreferenceChanged event, it is often the one that deadlocks your app when you create controls on a worker thread. Typically when the workstation is locked (press Win+L). Which cannot come to a good end when you use the controls I listed, the SystemEvents class tries to raise the event on the UI thread but of course cannot do this correctly when more than one thread has created them.
Also the kind of bug that can have a lasting effect, a splash screen for example can get the SystemEvents class to guess wrong about which thread is your UI thread. After which it then permanently raises the event on the wrong thread. Very ugly to diagnose, the deadlock is well hidden.