Reputation: 3787
I created a BackgroundWorker (mainBw
) in my UI (WPF) thread. It has an infinite loop where it sleeps for 1.5 sec and calls a function via Application.Current.Dispatcher.Invoke
which just outputs text from the "global" text
variable to a TextBox.
Also before the loop it created another (child
) BackgroundWorker which ReportsProgress, in ProgressChanged event handler it modifies the text
variable.
I thought that it will not work because there is no anything like WinForms Application.DoEvents() in the mainBw
loop so it can't process the event handler. But it works. Why?
Here is the code:
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows;
namespace WpfApplication6
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private BackgroundWorker mainBw = new BackgroundWorker();
private void Button_Click(object sender, RoutedEventArgs e)
{
mainBw.DoWork += MainBwOnDoWork;
mainBw.RunWorkerAsync();
btn.IsEnabled = false;
}
private string text = "abc";
private void MainBwOnDoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += BwOnDoWork;
bw.ProgressChanged += BwOnProgressChanged;
bw.WorkerReportsProgress = true;
bw.RunWorkerAsync();
while (true)
{
Thread.Sleep(1500);
text += " main ";
Application.Current.Dispatcher.Invoke(new Action(() => { WriteToUIThread(); }));
}
}
private void WriteToUIThread()
{
tbox.Text = DateTime.Now + " " + text + Environment.NewLine + tbox.Text;
}
private void BwOnProgressChanged(object sender, ProgressChangedEventArgs e)
{
text += e.UserState.ToString();
}
private void BwOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
while (true)
{
Thread.Sleep(3000);
(sender as BackgroundWorker).ReportProgress(0, "child");
}
}
}
}
// XAML
<Window x:Class="WpfApplication6.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Name="btn" Content="Button" HorizontalAlignment="Left" Height="105" Margin="43,47,0,0" VerticalAlignment="Top" Width="165" Click="Button_Click"/>
<TextBox Name="tbox" HorizontalAlignment="Left" Height="114" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="456" Margin="27,182,0,0"/>
</Grid>
</Window>
Upvotes: 0
Views: 309
Reputation: 941327
BackgroundWorker uses a universal way to get code to run on the UI thread, it uses the static SynchronizationContext.Current property to find a synchronization provider. ReportProgress() uses its Post() method to marshal the call.
If you run a Winforms app then the Current property will reference an instance of WindowsFormsSynchronizationContext class. Automatically installed when you create a Form or call Application.Run(). It uses Control.Begin/Invoke() to implement the Post and Send methods.
And if you run a WPF app then the Current property will reference an instance of DispatcherSynchronizationContext, it uses Dispatcher.Begin/Invoke().
So this just works automagically.
Upvotes: 2
Reputation: 6660
It works, because the BackgroundWorker
runs - as its name says - in a background thread independently fromo the main UI thread.
The ReportProgress
-event gets marshalled to the UI thread so that this event can easily be handled, without calling Invoke
-methods on the controls involved.
Contraray, the Application.DoEvents()
-method in WinForms allowed the process to handle other messages for perfoming long going operations in main thread, without using a background thread.
Upvotes: 0
Reputation: 203823
It works because the BackgroundWorker
does work in a background thread (hence the name). Since it's not running in the UI thread, it's not blocking the UI thread, it is just sending short methods to be run in the UI thread every once in a while.
That said, it's still not a particularly well designed approach to solving the problem. If you want to run some code every 3 seconds just use a Timer
instead. If you use the timer in the Forms
namespace it will fire it's event in the UI thread on your behalf.
Upvotes: 1