Understanding the WPF Dispatcher.BeginInvoke
Solution 1
Before coming down to your code behavior it's a prerequisite to understand the priorities of Dispatcher
. DispatcherPriority
is divided into ranges as shown in below image.
If you simply queue 4 actions to 4 above ranges on Dispatcher
. the Foreground
queue will get executed first, then the Background
and then in last Idle
queue. priority 0 will not get executed.
Now your code:
Three task are queued 1st in background
, 2nd in background
and 3rd in foreground
queue. So 3rd will get executed first. then 2nd task cause it has higher priority then 1st task. I hope that clears it.
Although some more observation will help you understand it better like, what if you have set the priorities as 7,8 and 9. So as this is a foreground queue, 7 will get executed first then 7 and then 8. One by one and exclusively in that order and while 7 is getting executed, 8 and 9 will wait, meaning foreground
queue will get executed synchronously to each another.
But Background
and Idle
queue will not behave in that way the where execution is asynchronous to other tasks and tasks will follow the priority. And first Background
and the Idle
queue.
Hope this explanation clarifies to some extent.
Solution 2
This is because the first MessageBox
is blocking the UI thread.
What Dispatcher.BeginInvoke()
is doing under the hood is taking your delegate and scheduling it to be run on the main UI thread during it's next idle period. However, MessageBox
will block whichever thread it is called from until it is closed. This means that the second MessageBox
cannot be displayed until the first is cleared because the UI thread scheduler sees that the thread is already in use (waiting for the first MessageBox
to be cleared) and can't execute the next delegate containing the second MessageBox
.
Vignesh.N
Follow Vignesh.N on Twitter Love it when I write beautiful recursive functions that simply work :) .NET C# #LINQ #Reflection #Heaven
Updated on September 23, 2020Comments
-
Vignesh.N over 3 years
I was under the impression that the
dispatcher
will follow the priority of the operations queued it and execute the operations based on the priority or the order in which the operation was added to the queue(if same priority) until I was told that this is no the case in case of theWPF UI dispatcher
.I was told that if a operation on the UI thread takes longer duration say a database read the UI dispatcher simple tries to execute next set of operations in the queue. I could not come to terms with it so decided to write a sample WPF application which contains a button and three rectangles, on click of the button, the rectangles are filled with different colors.
<StackPanel> <Button x:Name="FillColors" Width="100" Height="100" Content="Fill Colors" Click="OnFillColorsClick"/> <TextBlock Width="100" Text="{Binding Order}"/> <Rectangle x:Name="RectangleOne" Margin="5" Width="100" Height="100" Fill="{Binding BrushOne}" /> <Rectangle x:Name="RectangleTwo" Margin="5" Width="100" Height="100" Fill="{Binding BrushTwo}"/> <Rectangle x:Name="RectangleThree" Margin="5" Width="100" Height="100" Fill="{Binding BrushThree}"/> </StackPanel>
and in the code-behind
private void OnFillColorsClick(object sender, RoutedEventArgs e) { var dispatcher = Application.Current.MainWindow.Dispatcher; dispatcher.BeginInvoke(new Action(() => { //dispatcher.BeginInvoke(new Action(SetBrushOneColor), (DispatcherPriority)4); //dispatcher.BeginInvoke(new Action(SetBrushTwoColor), (DispatcherPriority)5); //dispatcher.BeginInvoke(new Action(SetBrushThreeColor), (DispatcherPriority)6); dispatcher.BeginInvoke(new Action(SetBrushOneColor)); dispatcher.BeginInvoke(new Action(SetBrushTwoColor)); dispatcher.BeginInvoke(new Action(SetBrushThreeColor)); }), (DispatcherPriority)10); } private void SetBrushOneColor() { Thread.Sleep(10 * 1000); Order = "One"; //MessageBox.Show("One"); BrushOne = Brushes.Red; } private void SetBrushTwoColor() { Thread.Sleep(12 * 1000); Order = "Two"; //MessageBox.Show("Two"); BrushTwo = Brushes.Green; } private void SetBrushThreeColor() { Thread.Sleep(15 * 1000); Order = "Three"; //MessageBox.Show("Three"); BrushThree = Brushes.Blue; } public string Order { get { return _order; } set { _order += string.Format("{0}, ", value); RaisePropertyChanged("Order"); } }
The commented code works as expected the methods are invoked based on the
DispatcherPriority
and I also get to see the screen refresh after each operation has been completed.Order
isOne, Two, Three
. Colors are drawn one after another.Now the working code where the
DispatcherPriority
is not mentioned ( I assume it would default toNormal
) the order is stillOne, Two, Three
but if I show aMessageBox
inside the methods, the
Thrid
popup is show first thenTwo
thenOne
but when I debug I could see the methods are
invoked in the expected order (IntelliTrace even shows that a message box is shown but I don't see it on the screen at that time and see it only after the last operation is finished.) its just that theMessageBox
es are shown in the reverse order.Is it because
MessageBox.Show
is a blocking call and the operation are cleared after the message has been closed.
Even then the order of theMessageBox
should also beOne
,Two and
Three` ?