ReactiveUI 9:将列表绑定到 WPF 视图
ReactiveUI 9: binding lists to a WPF view
在 ReactiveUI 9 中,ReactiveList
已被弃用,取而代之的是 DynamicData (Blog post)。我目前正在尝试更新我的代码以使用 SourceList
。我能够让 ViewModel 工作,但似乎在 WPF 中使用 SourceList
作为绑定数据源并不那么容易。
我的第一次尝试是创建绑定,就像在以前版本的 ReactiveUI 中所做的那样:
this.OneWayBind(ViewModel, vm => vm.MyList, v => v.MyListView.ItemsSource);
这不起作用,因为 SourceList
不可枚举(无法将 DynamicData.ISourceList 转换为 System.Collections.IEnumerable)
我的第二次尝试是使用列表的 Items
属性。
this.OneWayBind(ViewModel, vm => vm.MyList.Items, v => v.MyListView.ItemsSource);
这不起作用,因为 Items
getter 在内部创建了列表的副本,这意味着列表中的更改不会反映在视图中。
我的第三次尝试是使用 Bind
方法创建一个 ReadOnlyObservableCollection
。
我不想在视图模型中执行此操作,因为那样我就必须为每个视图模型中的每个列表添加第二个列表 属性,这会扰乱我的代码,违反了 DRY 原则。此外,要绑定的列表类型取决于所使用的视图框架。 (例如:WinForms 使用 BindingList
代替)
此外,我的视图的视图模型可能会更改,这意味着在设置新视图模型时必须清理并替换生成的绑定和列表。这给了我以下片段:
this.WhenAnyValue(v => v.ViewModel.VisibleInputs)
.Select(l =>
{
var disposer = l.Connect().Bind(out var list).Subscribe();
return (List: list, Disposer: disposer);
})
.PairWithPreviousValue()
.Do(p => p.OldValue.Disposer?.Dispose()) // Cleanup the previous list binding
.Select(p => p.NewValue.List)
.BindTo(this, v => v.InputsList.ItemsSource);
这工作正常,但非常冗长。我可以为此创建一个扩展方法,但也许有一种 better/built-in 方法来使用 DynamicData 进行 WPF 列表绑定?
我认为这个想法是视图绑定到调度程序线程上的 IObservableCollection<T>
并且 SourceList<T>
将流生成的对象提供给这个线程,例如:
public class MainViewModel : ReactiveObject
{
private SourceList<int> _myList { get; } = new SourceList<int>();
private readonly IObservableCollection<int> _targetCollection = new ObservableCollectionExtended<int>();
public MainViewModel()
{
Task.Run(()=>
{
for (int i = 0; i < 100; ++i)
{
_myList.Add(i);
System.Threading.Thread.Sleep(500);
}
});
_myList.Connect()
.ObserveOnDispatcher()
.Bind(_targetCollection)
.Subscribe();
}
public IObservableCollection<int> TargetCollection => _targetCollection;
}
查看:
this.OneWayBind(ViewModel, vm => vm.TargetCollection, v => v.MyListView.ItemsSource);
您可以阅读更多相关信息 here。
在 ReactiveUI 9 中,ReactiveList
已被弃用,取而代之的是 DynamicData (Blog post)。我目前正在尝试更新我的代码以使用 SourceList
。我能够让 ViewModel 工作,但似乎在 WPF 中使用 SourceList
作为绑定数据源并不那么容易。
我的第一次尝试是创建绑定,就像在以前版本的 ReactiveUI 中所做的那样:
this.OneWayBind(ViewModel, vm => vm.MyList, v => v.MyListView.ItemsSource);
这不起作用,因为 SourceList
不可枚举(无法将 DynamicData.ISourceList 转换为 System.Collections.IEnumerable)
我的第二次尝试是使用列表的 Items
属性。
this.OneWayBind(ViewModel, vm => vm.MyList.Items, v => v.MyListView.ItemsSource);
这不起作用,因为 Items
getter 在内部创建了列表的副本,这意味着列表中的更改不会反映在视图中。
我的第三次尝试是使用 Bind
方法创建一个 ReadOnlyObservableCollection
。
我不想在视图模型中执行此操作,因为那样我就必须为每个视图模型中的每个列表添加第二个列表 属性,这会扰乱我的代码,违反了 DRY 原则。此外,要绑定的列表类型取决于所使用的视图框架。 (例如:WinForms 使用 BindingList
代替)
此外,我的视图的视图模型可能会更改,这意味着在设置新视图模型时必须清理并替换生成的绑定和列表。这给了我以下片段:
this.WhenAnyValue(v => v.ViewModel.VisibleInputs)
.Select(l =>
{
var disposer = l.Connect().Bind(out var list).Subscribe();
return (List: list, Disposer: disposer);
})
.PairWithPreviousValue()
.Do(p => p.OldValue.Disposer?.Dispose()) // Cleanup the previous list binding
.Select(p => p.NewValue.List)
.BindTo(this, v => v.InputsList.ItemsSource);
这工作正常,但非常冗长。我可以为此创建一个扩展方法,但也许有一种 better/built-in 方法来使用 DynamicData 进行 WPF 列表绑定?
我认为这个想法是视图绑定到调度程序线程上的 IObservableCollection<T>
并且 SourceList<T>
将流生成的对象提供给这个线程,例如:
public class MainViewModel : ReactiveObject
{
private SourceList<int> _myList { get; } = new SourceList<int>();
private readonly IObservableCollection<int> _targetCollection = new ObservableCollectionExtended<int>();
public MainViewModel()
{
Task.Run(()=>
{
for (int i = 0; i < 100; ++i)
{
_myList.Add(i);
System.Threading.Thread.Sleep(500);
}
});
_myList.Connect()
.ObserveOnDispatcher()
.Bind(_targetCollection)
.Subscribe();
}
public IObservableCollection<int> TargetCollection => _targetCollection;
}
查看:
this.OneWayBind(ViewModel, vm => vm.TargetCollection, v => v.MyListView.ItemsSource);
您可以阅读更多相关信息 here。