通常,使用 MVVM 呈现IObservable<T> 的最直接方法是制作一个如下所示的常规视图模型对象,然后手动将其订阅到可观察对象。订阅应使用.ObserveOn(SynchronizationContext.Current) 执行,以在 UI 线程中分派所有通知。反过来,此时同步上下文应该存在(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 在视图模型中生成自动属性。
视图模型:
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();
}
即使调度程序尚未启动且SynchronizationContext.Current 为空,此处ObserveOnDispatcher() 调用仍有效。
调用者方法:
[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>