In ReactiveUI 9, ReactiveList
has been deprecated in favor of DynamicData (Blog post). I am currently in the process of trying to update my code to use SourceList
. I was able to get the ViewModel to work, however it seems that using SourceList
as a binding datasource in WPF is not as easy.
My first attempt was to create the binding as was done in previous versions of ReactiveUI:
this.OneWayBind(ViewModel, vm => vm.MyList, v => v.MyListView.ItemsSource);
This doesn't work, because SourceList
is not enumerable (Can't convert DynamicData.ISourceList to System.Collections.IEnumerable)
My second attempt was to use the Items
property of the list.
this.OneWayBind(ViewModel, vm => vm.MyList.Items, v => v.MyListView.ItemsSource);
This doesn't work because the Items
getter internally creates a copy of the list, which means changes in the list won't be reflected in the view.
My third attempt was to use the Bind
method to create a ReadOnlyObservableCollection
.
I don't want to do this in the viewmodel, because then I would have to add a second list property for every list in every viewmodel which clutters up my code, violating the DRY principle. Furthermore, the type of list to bind to depends on the view framework that is used. (for example: WinForms uses BindingList
instead)
Also, the viewmodel of my view might change, which means that the resulting binding and list must be cleaned up and replaced when a new viewmodel is set. This gives me the following snippet:
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);
This works fine, but its pretty verbose. I could create an extension method for this, but maybe there is a better/built-in way to do WPF list binding with DynamicData?
I think the idea is that the view binds to an IObservableCollection<T>
on the dispatcher thread and that the SourceList<T>
feeds this one with the objects that the stream produces, e.g.:
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;
}
View:
this.OneWayBind(ViewModel, vm => vm.TargetCollection, v => v.MyListView.ItemsSource);
You can read more about this here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With