I'm learning ReactiveUI at the moment, and I'm struggling to figure out how to bind a collection to a ListBox in WPF. I already understand the MVVM pattern, and I was able to do this very thing in MVVM Light some years ago, but ReactiveUI is proving more difficult. I haven't been able to find too many simple examples, just convoluted ones that don't do a great job at demonstrating the basics. Also, a lot of the answers I've found on Stack Overflow are very old, so they don't use the new Dynamic Data libraries that ReactiveUI recommends nowadays.
I'm working in a very simple WPF application while I try to learn the ropes. I have a simple view called ListBoxView:
<rxui:ReactiveUserControl x:Class="MvvmSandbox.Views.ListBoxView"
x:TypeArguments="vm:ListBoxViewModel"
xmlns:rxui="http://reactiveui.net"
xmlns:vm="clr-namespace:MvvmSandbox.ViewModels"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MvvmSandbox.Views"
mc:Ignorable="d"
d:DesignHeight="250" d:DesignWidth="500">
<ListBox x:Name="Strings" />
</rxui:ReactiveUserControl>
Here's the code-behind:
using MvvmSandbox.ViewModels;
using ReactiveUI;
namespace MvvmSandbox.Views
{
public partial class ListBoxView : ReactiveUserControl<ListBoxViewModel>
{
public ListBoxView()
{
InitializeComponent();
ViewModel = new ListBoxViewModel();
this.WhenActivated(disposable =>
{
this.OneWayBind(ViewModel, vm => vm.Strings, v => v.Strings.ItemsSource);
});
}
}
}
And here's the view model:
using ReactiveUI;
using System.Collections.ObjectModel;
namespace MvvmSandbox.ViewModels
{
public class ListBoxViewModel : ReactiveObject
{
public ObservableCollection<string> Strings => _strings;
private ObservableCollection<string> _strings;
public ListBoxViewModel()
{
_strings = new ObservableCollection<string>()
{
"This is a test of the Emergency Broadcast System.",
"This is only a test."
};
}
}
}
The view does come out with the items, but they're collapsed to their minimum height and don't display the strings themselves; you can see the blue bounding box for the first item here.
I'm not the best at ReactiveUI (or even WPF), so I simplified a bit by converting the 'control' to a 'mainwindow'. Your problem seeing the output might just be that you didn't set the collection as the itemsource for the ListBox. I also used an ItemTemplate in my xaml code. Finally, I used the ReactiveUI suggested collection types instead of a just a simple ObservableCollection, and did a bit of fancy timed changes to the list so you can better see how that works.
see: https://www.reactiveui.net/docs/handbook/collections.html
MainWindow.xaml:
<rxui:ReactiveWindow x:Class="MvvmSandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:rxui="http://reactiveui.net"
xmlns:vm="clr-namespace:MvvmSandbox.ViewModels"
Title="MainWindow"
Width="200"
Height="250"
x:TypeArguments="vm:ListBoxViewModel"
mc:Ignorable="d">
<StackPanel>
<ListBox x:Name="stringLB" HorizontalAlignment="Center">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=.}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</rxui:ReactiveWindow>
MainWindow.xaml.cs
using System.Reactive.Disposables;
using ReactiveUI;
using MvvmSandbox.ViewModels;
namespace MvvmSandbox
{
public partial class MainWindow : ReactiveWindow<ListBoxViewModel>
{
public MainWindow() {
InitializeComponent();
ViewModel = new ListBoxViewModel();
this.WhenActivated(d => {
this.OneWayBind(ViewModel, vm => vm.MyStrings,
view => view.stringLB.ItemsSource).DisposeWith(d);
});
}
}
}
ListBoxViewModel.cs
using System.Collections.ObjectModel;
using System.Reactive.Linq;
using DynamicData;
using ReactiveUI;
namespace MvvmSandbox.ViewModels
{
public class ListBoxViewModel : ReactiveObject
{
private readonly SourceList<string> _myStrings;
public readonly ReadOnlyObservableCollection<string> MyStrings;
public ListBoxViewModel() {
_myStrings = new SourceList<string>();
_myStrings.Connect()
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(out MyStrings)
.Subscribe();
// initial list
_myStrings.Add("This is a test");
// add items one sec at a time
Observable.Interval(TimeSpan.FromSeconds(1))
.Take(3)
.Select(i => $"This is test {i+1}")
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(x => _myStrings.Add(x));
// remove an item
Observable.Interval(TimeSpan.FromSeconds(1))
.Skip(3)
.Take(1)
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(x => _myStrings.RemoveAt(0));
}
}
}