Last active
April 16, 2024 11:22
-
-
Save StephenCleary/4248e50b4cb52b933c0d to your computer and use it in GitHub Desktop.
ObservableProgress
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Reactive.Linq; | |
using System.Threading; | |
/// <summary> | |
/// Helper methods for using observable <see cref="IProgress{T}"/> implementations. These are hot observables. | |
/// </summary> | |
public static class ObservableProgress | |
{ | |
/// <summary> | |
/// Creates an observable and an <see cref="IProgress{T}"/> implementation that are linked together, suitable for UI consumption. | |
/// When progress reports are sent to the <see cref="IProgress{T}"/> implementation, they are sampled according to <paramref name="sampleInterval"/> and then forwarded to the UI thread. | |
/// This method must be called from the UI thread. | |
/// </summary> | |
/// <typeparam name="T">The type of progress reports.</typeparam> | |
/// <param name="sampleInterval">How frequently progress reports are sent to the UI thread.</param> | |
public static (IObservable<T> Observable, IProgress<T> Progress) CreateForUi<T>(TimeSpan? sampleInterval = null) | |
{ | |
var (observable, progress) = Create<T>(); | |
observable = observable.Sample(sampleInterval ?? TimeSpan.FromMilliseconds(100)) | |
.ObserveOn(SynchronizationContext.Current); | |
return (observable, progress); | |
} | |
/// <summary> | |
/// Creates an observable and an <see cref="IProgress{T}"/> implementation that are linked together. | |
/// When progress reports are sent to the <see cref="IProgress{T}"/> implementation, they are immediately and synchronously sent to the observable. | |
/// </summary> | |
/// <typeparam name="T">The type of progress reports.</typeparam> | |
public static (IObservable<T> Observable, IProgress<T> Progress) Create<T>() | |
{ | |
var progress = new EventProgress<T>(); | |
var observable = Observable.FromEvent<T>(handler => progress.OnReport += handler, handler => progress.OnReport -= handler); | |
return (observable, progress); | |
} | |
private sealed class EventProgress<T> : IProgress<T> | |
{ | |
public event Action<T> OnReport; | |
void IProgress<T>.Report(T value) => OnReport?.Invoke(value); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Sample program is WPF, but could be any UI: WPF, WinForms, Xamarin Forms, WinRT, WinPhone, or Silverlight. | |
private async void Button_Click(object sender, RoutedEventArgs e) | |
{ | |
var (observable, progress) = ObservableProgress.CreateForUi<int>(); | |
using (observable.Subscribe(UpdateUi)) | |
await Task.Run(() => Solve(progress)); | |
void UpdateUi(int value) | |
{ | |
// Update UI (if using MVVM, you should update ViewModels here, not set UI control contents directly) | |
Label.Content = value; | |
} | |
} | |
private void Solve(IProgress<int> progress) | |
{ | |
int value = 0; | |
while (true) | |
{ | |
value++; | |
progress?.Report(value); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment