Reputation: 183
I do have a WPF application displaying data (computed from another thread) in real-time. But my UI components (here a TextBlock
) update very slowly.
I use traditional data binding with PropertyChanged
notifications.
xaml :
<TextBlock
Foreground="DarkGray"
Text="{Binding Path=ContactSurface, StringFormat='{}{0:0.00} cm²'}"/>
code-behind (no, this is not MVVM, shame on me) :
private double _contactSurface;
public double ContactSurface
{
get { return _contactSurface; }
set { _contactSurface = value; RaisePropertyChanged("ContactSurface"); }
}
public void Compute() // external thread about 10 Hz
{
ContactSurface = (double)nbSensorsNotNulls * DataSource.SensorSurface * 0.01;
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Render, new Action(() => { })); // does not change a thing
//Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, new Action(() => { })); // crash : Cannot perform this operation while dispatcher processing is suspended.
//UpdateLayout(); // crash : The calling thread can not access this object because a different thread owns it
//InvalidateVisual(); // crash : The calling thread can not access this object because a different thread owns it
}
I've tried some things I've found around the web at the end of Compute()
with very mean results detailes
Upvotes: 2
Views: 545
Reputation: 38094
If your execute some time-consuming work in another thread, then you must synchronize a result of another thread with UI thread. To synchronize two threads(new thread and UI thread), it is necessary to use Dispatcher.
Only one thread can modify the UI thread. But how do background threads interact with the user? A background thread can ask the UI thread to perform an operation on its behalf. It does this by registering a work item with the Dispatcher of the UI thread. The Dispatcher class provides two methods for registering work items: Invoke and BeginInvoke. Both methods schedule a delegate for execution. Invoke is a synchronous call – that is, it doesn’t return until the UI thread actually finishes executing the delegate. BeginInvoke is asynchronous and returns immediately.
For example:
Task.Run(()=> {
var result = (double)nbSensorsNotNulls * DataSource.SensorSurface * 0.01;
Thread.Sleep(5000);//imitate time consuming work
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Render,
new Action(() => {
ContactSurface=result;}));
});
In the above example, we create a new thread(Task.Run(...)
) and synchronize result of new thread with UI thread(Dispatcher.BeginInvoke(...)
)
Upvotes: 2