olivier
olivier

Reputation: 183

Refresh TextBlock quicker from another thread

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

Answers (1)

StepUp
StepUp

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.

As MSDN says:

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

Related Questions