Updated question:
I'm having a long running async
method, I've built a WPF GUI to execute it. GUI is using ReactiveUI
. I need to have some UI Feedback for users when the async
process is running. The first click on the button always hangs the GUI. I've noticed in the Output window of Visual Studio when I ran my application in Debug mode:: the first time when the button was clicked, clrhost loaded a lot of DLLs (Microsoft.EntityFrameworkCore.xxx.dll
, Microsoft.Extensions.[Caching/DependencyInjection/...].xxx.dll
...) and that seemed to be the culprit which made the GUI looked like it was hang for a couple seconds.
Looks like the DLLs were only loaded when required for the first time the library was called. Is there anyway to give GUI the feedback before those DLLs loaded?
Original question:
I'm pretty new to ReactiveUI. I'm creating an UI when user click a button, it will execute a long running asynchronous command. I want to provide some feedback on the GUI when the process starts and finishes.
This question mentioned Subject
, so this is what I came up with:
ViewModel:
public class MainWindowVM : ReactiveObject
{
public enum ProcessStatus
{
Running,
Completed,
Error
}
private readonly ReplaySubject<ProcessStatus> _exportCSVCommandReplayStatus;
public readonly ReactiveCommand<Unit, Unit> ExportDatabaseToCSV;
private readonly ObservableAsPropertyHelper<string> _csvExportMessage;
public string CSVExportMessage => _csvExportMessage.Value;
private readonly ObservableAsPropertyHelper<ProcessStatus> _exportDatabaseToCSVStatus;
public ProcessStatus ExportDatabaseToCSVStatus => _exportDatabaseToCSVStatus.Value;
public MainWindowVM()
{
_exportCSVCommandReplayStatus = new ReplaySubject<ProcessStatus>(1);
_exportDatabaseToCSVStatus = _exportCSVCommandReplayStatus
.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, nameof(ExportDatabaseToCSVStatus), ProcessStatus.Completed);
ExportDatabaseToCSV = ReactiveCommand.CreateFromTask(ExportDatabaseToCSVAsync, canExecute:
this.WhenAnyValue(x => x.ExportDatabaseToCSVStatus, status => status != ProcessStatus.Running));
// error handlers
ExportDatabaseToCSV.ThrownExceptions.Subscribe(ex => {
_exportCSVCommandReplayStatus.OnNext(ProcessStatus.Error);
//ErrorHandler(ex);
});
_exportCSVCommandReplayStatus
.Select(status => status switch
{
ProcessStatus.Running => "CSV exporting...",
ProcessStatus.Error => "CSV export error! Check log for more details",
ProcessStatus.Completed => "CSV export completed.",
_ => ""
})
.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.CSVExportMessage, out _csvExportMessage);
}
private async Task ExportDatabaseToCSVAsync()
{
_exportCSVCommandReplayStatus.OnNext(ProcessStatus.Running);
await Util.ExportDBToCSV();
_exportCSVCommandReplayStatus.OnNext(ProcessStatus.Completed);
}
}
And then make all the bindings accordingly in the View. Simple demonstration: a button, a progress bar, and a label
...
this.BindCommand(ViewModel,
vm => vm.ExportDatabaseToCSV,
v => v.BtnExportDatabaseCSV)
.DisposeWith(d);
this.OneWayBind(ViewModel,
vm => vm.ExportDatabaseToCSVStatus,
v => v.PrgBarCSVExporting.Visibility,
status => status == ProcessStatus.Running
? Visibility.Visible
: Visibility.Collapsed)
.DisposeWith(d);
this.OneWayBind(ViewModel,
vm => vm.CSVExportMessage,
v => v.LblCSVExportingStatusMessage.Content)
.DisposeWith(d);
...
The problem I ran into was: when the button was clicked for the first time, the UI was blocked, and completed message displayed suddenly after a while.
All the subsequence clicks work as expected (i.e. button is disabled, progress bar showing, message update...)
Am I doing it wrongly? What is the recommended way to deal with this scenario in ReactiveUI?
Subscribing to IsExecuting
of the ReactiveCommand
produced the same behaviour.