Часто самый простой способ представить IObservable<T>
с MVVM - это создать обычный объект модели представления, подобный представленному ниже, и вручную подписать его на наблюдаемый. Подписка должна быть выполнена с использованием .ObserveOn(SynchronizationContext.Current)
для отправки всех уведомлений в потоке пользовательского интерфейса. В свою очередь, контекст синхронизации должен существовать в этот момент (SynchronizationContext.Current
равен нулю до new Application().Run(mainWindow)
). Смотрите образец ниже:
public class ViewModel : INotifyPropertyChanged
{
private int _property1;
public int Property1
{
get => _property1;
set
{
_property1 = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Property1)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Вид:
<Window x:Class="ConsoleApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBox Text="{Binding Property1}" />
</Window>
Метод вызывающего абонента:
[STAThread]
static void Main()
{
var model = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1)).Take(10);
var viewModel = new ViewModel();
var mainWindow = new MainWindow
{
DataContext = viewModel
};
IDisposable subscription = null;
mainWindow.Loaded += (sender, e) =>
subscription = model
.ObserveOn(SynchronizationContext.Current) // Dispatch all the events in the UI thread
.Subscribe(item => viewModel.Property1 = item);
new Application().Run(mainWindow);
subscription?.Dispose();
}
Обновление
Другим вариантом является использование ReactiveUI.WPF и еще более кратким - ReactiveUI.Fody для создания авто-свойств в модели представления.
View-модель:
public class ViewModel : ReactiveObject, IDisposable
{
private readonly IDisposable subscription;
public ViewModel(IObservable<long> source)
{
subscription = source
.ObserveOnDispatcher()
.ToPropertyEx(this, x => x.Property1);
}
// To be weaved by Fody
public extern long Property1 { [ObservableAsProperty]get; }
// Unsubscribe from the source observable
public void Dispose() => subscription.Dispose();
}
Здесь ObserveOnDispatcher()
вызов работает, даже если диспетчер не был запущен и SynchronizationContext.Current
равен нулю.
Метод вызова:
[STAThread]
static void Main()
{
new Application().Run(
new MainWindow
{
DataContext = new ViewModel(
Observable.Timer(
TimeSpan.Zero,
TimeSpan.FromSeconds(1))
.Take(20))
});
}
FodyWeavers.xml рядом с файлом решения:
<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
<ReactiveUI />
</Weavers>