albertomiles
albertomiles

Reputation: 21

Updating UI in batches with RX c#

I'm having an issue with updating WPF UI with the RX. Currently I have a class that has an event which is called within its functions. Event is subscribed from the UI thread and updates the UI like below :

SomeClass.cs

public partial class SomeClass
{
    public delegate Task ProgressUpdate(string value);

    public delegate Task BarUpdate(int value);

    public event ProgressUpdate OnProgressUpdateList;

    public event BarUpdate OnProgressUpdateBar;

    public async Task DoSomething() 
    {
      // execute code
       <some code>
      // update UI

      if (OnProgressUpdateList != null)
       {       
           OnProgressUpdateList(update);             
       }       
    }
 }

And in MainWindow.xaml

var someClass = new SomeClass();
someClass.OnProgressUpdateList += Export_OnProgressUpdateList;
someClass.OnProgressUpdateBar += Export_OnProgressUpdateBar;


private async Task Export_OnProgressUpdateList(string text)
{
     await Dispatcher.InvokeAsync(() =>
       {
           OutputLog.AppendText(text);
           OutputLog.AppendText(Environment.NewLine);
           OutputLog.ScrollToEnd();
        });
 }

This code works except the program processes huge number of files and I'm assuming this is why the UI becomes frozen very quickly (I see the updates being done in the first half a second). I searched for a way around this and I came into a solution to use RX for batching the UI calls. I've searched through several SO posts but I couldn't find an answer on how to correctly implements this (or convert C# events to RX observables) when I call those events from the class and subscribe to this event from outside that class. Can someone help me understand this?

Upvotes: 0

Views: 136

Answers (1)

albertomiles
albertomiles

Reputation: 21

I'm posting an answer to myself as I couldn't get one here and I finally figured it out so for anyone looking for that in the future - here you go:

public partial class SomeClass {

public Subject<string> outputLogSubject = new Subject<string>();

public IObservable<string> OutputLog => outputLogSubject.AsObservable();

//Add string for collection updating UI
outputLogSubject.OnNext(string);


//After finishing the work you can call outputLogSubject.OnCompleted() to stop buffering
outputLogSubject.OnCompleted();

}

It needs to be added in the class that will be calling the executing the work.

Below needs to be added in the UI thread after initialization and BEFORE processing work :

var buffer = someClass.OutputLog.Buffer(TimeSpan.FromMilliseconds(1000), 6);
var chunked = buffer.ObserveOnDispatcher(DispatcherPriority.Background);
var update = chunked.Subscribe(name =>
{
foreach (var item in name)
  {
OutputLog.AppendText(item);
  }
OutputLog.ScrollToEnd();
});

This allowed me to keep the UI responsive to the point of seeing the output log is real time

Upvotes: 1

Related Questions