0

Assuming that a client app gets data from a server nearly in real time. What is the more efficient way to continuously update the UI based on the retrieved data. Think of multiple xaml controls, like texts that show numbers. Those get updated as long as the application is running. They never stop unless the user decides it. (let's say by pressing a stop button or exit the app) Below I have a simple example utilizing async and await keywords. Is that a good way for my scenario? Or for example BackgroundWorker would be a better way?

private async void Button_Click_Begin_RT_Update(object sender, RoutedEventArgs e)
{
    while(true)
        textField1.Text = await DoWork();
}

Task<string> DoWork()
{
    return Task.Run(() =>
    {
        return GetRandomNumberAsString();
    });
}

*for the sake of simplicity I use code-behind and not mvvm in my example

Il Vic
  • 5,576
  • 4
  • 26
  • 37
gts13
  • 1,048
  • 1
  • 16
  • 29
  • It's been a while since I used WPF but I would have thought binding to a DependencyProperty was the way to go. These 2 posts should shunt you in the right direction: https://stackoverflow.com/questions/19981966/wpf-xaml-binding-to-object-created-in-code-behind, https://stackoverflow.com/questions/5824600/databind-from-xaml-to-code-behind – Adam Benson May 31 '17 at 13:14
  • 1
    The core idea behind WPF is using databinding and view model classes so that you *never* call the controls directly in code behind. Therefore, if I were you I would create a viewmodel class, implement `INotifyPropertyChanged` interface, bind the properties to related controls, and then update the viewmodel class instance by using async `Task`s. – mcy May 31 '17 at 13:17
  • @mcy for the sake of simplicity I didn't write my code based on MVVM. However I do use mvvm in my project. – gts13 May 31 '17 at 13:21
  • So what problem are you having with your code? What about it isn't working? – Servy May 31 '17 at 13:22
  • @Servy just wondering what is the most efficient way to implement my scenario! – gts13 May 31 '17 at 13:25
  • 3
    @gts13 oh ok. `Task` vs `BackgroundWorker` is a topic that attracted a lot of attention and you may find good posts like https://blog.stephencleary.com/2013/09/taskrun-vs-backgroundworker-conclusion.html or https://stackoverflow.com/questions/12414601/async-await-vs-backgroundworker Personally I select `Task` almost all of the times. – mcy May 31 '17 at 13:25
  • 1
    @gts13 And you already have a solution. Either it's working perfectly for you, in which case, you have no problem, or it isn't, and you need to be specific about how it's failing to meet your needs. – Servy May 31 '17 at 13:27

1 Answers1

2

Your code is more or less OK if your GetRandomNumberAsString() takes at least 15ms to complete.

If it takes less than that, and you want to minimize update latency i.e. you don't want to just wait, you might want to (1) replace your per-operation Task.Run with an endless loop that completely runs in a background thread (2) Implement throttling mechanism in that loop, and only update your GUI (using e.g. Dispatcher.BeginInvoke()) at around 30-60Hz.

P.S. The exact mechanism how you update your GUI (databinding + INotifyPropertyChanged, or directly like in your code) is not relevant for performance.

Update: here's example (untested)

static readonly TimeSpan updateFrequency = TimeSpan.FromMilliseconds( 20 );
void ThreadProc()
{
    Stopwatch sw = Stopwatch.StartNew();
    while( true )
    {
        string val = GetRandomNumberAsString();
        if( sw.Elapsed < updateFrequency )
            continue;   // Too early to update
        sw.Restart();
        Application.Current.Dispatcher.BeginInvoke( () => { textField1.Text = val; } );
    }
}
Soonts
  • 20,079
  • 9
  • 57
  • 130
  • can you please provide some sample code or a link to take a look, especially the "throttling mechanism in that loop" looks a bit tricky to me – gts13 May 31 '17 at 13:29
  • I have a 75Hz monitor. Some people have 144Hz monitors. Ideally your UI updates every paint cycle. – Ian Boyd May 31 '17 at 14:05
  • @IanBoyd For very latency-sensitive apps, CompositionTarget.Rendering is indeed better. But it’s harder to implement correctly. The event may be called several times per frame, and you only need to update GUI if your data has changed since the previous call. For most apps, ~30-50Hz update throttling is fine. – Soonts May 31 '17 at 14:17
  • "Application" does not contain definition for "Current". – user2188329 Apr 04 '20 at 19:27