Search code examples
c#wpfreactiveuidynamic-data

WPF ReactiveUI binding Listbox.ItemsSource to ReadOnlyObservableCollection not working


In my WPF+ReactiveUI+DynamicData project I can't make binding of ListBox.ItemsSource to ReadOnlyObservableCollection working. I successfully populate data from by backend service to my ViewModel

public class RecordingsListViewModel: ReactiveObject
    {
        private readonly ReadOnlyObservableCollection<RecordingViewModel> _recordings;
        public ReadOnlyObservableCollection<RecordingViewModel> Recordings => _recordings;

        public RecordingsListViewModel(IRecordingsService recordindsService)
        {
            recordindsService.RecordingsList.Connect()
                .Transform(rec => new RecordingViewModel(rec as IRecordingModel))
                .Bind(out _recordings)
                .Subscribe();
        }
    }

My View:

<reactiveui:ReactiveUserControl x:Class="MyProject.Views.RecordingsListView"
                                x:TypeArguments="vm:RecordingsListViewModel"
             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:vm="clr-namespace:MyProject.ViewModels"
             xmlns:reactiveui="http://reactiveui.net"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">

    <ListBox x:Name ="RecordingsListBox" HorizontalContentAlignment="Stretch"/>

</reactiveui:ReactiveUserControl>

In my code behind I'm trying to bind but it doesn't work

    {
        public static readonly DependencyProperty ViewModelProperty = DependencyProperty
        .Register(nameof(ViewModel), typeof(RecordingsListViewModel), typeof(RecordingsListView));

        public RecordingsListViewModel ViewModel
        {
            get => (RecordingsListViewModel)GetValue(ViewModelProperty);
            set => SetValue(ViewModelProperty, value);
        }

        object IViewFor.ViewModel
        {
            get => ViewModel;
            set => ViewModel = (RecordingsListViewModel)value;
        }

        public RecordingsListView()
        {
            ViewModel = Locator.Current.GetService<RecordingsListViewModel>();
            InitializeComponent();

            this.WhenActivated(d =>
            {
                this.OneWayBind(ViewModel,
                   vm => ViewModel.Recordings,
                   view => view.RecordingsListBox.ItemsSource).DisposeWith(d);
            });
        }
    }

There is an error in output, but it is not crashing app

PropertyBinderImplementation: view.RecordingsListBox.ItemsSource Binding received an Exception! - System.NotSupportedException: Unsupported expression of type 'Constant'. Did you miss the member access prefix in the expression?
   at ReactiveUI.ExpressionMixins.GetExpressionChain(Expression expression) in /_/src/ReactiveUI/Mixins/ExpressionMixins.cs:line 78
   at ReactiveUI.ReactiveNotifyPropertyChangedMixin.SubscribeToExpressionChain[TSender,TValue](TSender source, Expression expression, Boolean beforeChange, Boolean skipInitial, Boolean suppressWarnings) in /_/src/ReactiveUI/Mixins/ReactiveNotifyPropertyChangedMixin.cs:line 140
   at ReactiveUI.WhenAnyMixin.WhenAnyDynamic[TSender,TRet](TSender sender, Expression property1, Func`2 selector) in /_/src/ReactiveUI/VariadicTemplates.cs:line 99
   at ReactiveUI.Reflection.<>c__DisplayClass15_0`2.<ViewModelWhenAnyValue>b__2(Object x) in /_/src/ReactiveUI/Expression/Reflection.cs:line 407
   at System.Reactive.Linq.ObservableImpl.Select`2.Selector._.OnNext(TSource value) in /_/Rx.NET/Source/src/System.Reactive/Linq/Observable/Select.cs:line 39

I'm googling for this error and relevant examples for almost 16 hours, tried lots of code adjustments without any luck. Any ideas much appreciated


Solution

  • Many thanks to Glenn Watson and
    Đøharrrck comments. I finally fixed it. The problem was in wrong access to viewModel in binding. Was:

    this.OneWayBind(ViewModel,
                       vm => ViewModel.Recordings,
                       view => view.RecordingsListBox.ItemsSource).DisposeWith(d);
    

    Should be:

    this.OneWayBind(ViewModel,
                       vm => vm.Recordings,
                       view => view.RecordingsListBox.ItemsSource).DisposeWith(d);