Search code examples
wpflinqdata-bindingprism

Prism Binding to an ObservableCollection with no Setter


I'm having an issue with a XAML DataGrid which binds to a ObservableCollection. I have 3 classes, A,B,C. The idea is that the ObservableCollection for C will be generated by using Linq to query A and B.

Classes

public class A
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    public class B
    {
        public int Id { get; set; }
        public int Age { get; set; }
    }
    public class C
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

DataContext Class

private ObservableCollection<A> _as;
        public ObservableCollection<A> As
        {
            get => _as;
            set { SetProperty(ref _as, value); }
        }

        private ObservableCollection<B> _bs;
        public ObservableCollection<B> Bs
        {
            get => _bs;
            set { SetProperty(ref _as, value);  }
        }

        public ObservableCollection<C> Cs
        {
            get
            {
                var query = from x in As
                            join y in Bs
                            on x.Id equals y.Id
                            select new C() { Name = x.Name, Age = y.Age };

                return new ObservableCollection<C>(query);
            }
        }

MyView.xaml

<DataGrid ItemsSource="{Binding Cs}" Grid.Row="1"/>

If I update the ObservableCollection "As" by removing an item, I can see via a breakpoint that ObservableCollection "Cs" is also updated to reflect the change, but the Datagrid on the UI stays the same. Any help of pointers here would be great, thanks


Solution

  • You have to RaiseNotifyPropertyChanged when As or Bs change to trigger the update of the binding to Cs:

        private ObservableCollection<A> _as;
        public ObservableCollection<A> As
        {
            get => _as;
            set
            { 
                if (SetProperty(ref _as, value)) 
                    RaisePropertyChanged(nameof(Cs));
            }
        }
    
        private ObservableCollection<B> _bs;
        public ObservableCollection<B> Bs
        {
            get => _bs;
            set 
            { 
                if (SetProperty(ref _as, value))
                    RaisePropertyChanged(nameof(Cs));
            }
        }
    
        public IEnumerable<C> Cs
        {
            get
            {
                var query = from x in As
                            join y in Bs
                            on x.Id equals y.Id
                            select new C() { Name = x.Name, Age = y.Age };
    
                return query;
            }
        }
    

    Also, you want to RaisePropertyChanged whenever As's or Bs's content changes without the whole collection being replaced, i.e.

        public ObservableCollection<A> As
        {
            get => _as;
            set
            { 
                if (_as != null)
                    _as.CollectionChanged -= NotifyCs;
                if (SetProperty(ref _as, value))
                    RaisePropertyChanged(nameof(Cs));
                if (_as != null)
                    _as.CollectionChanged += NotifyCs;
            }
        }
    
        private void NotifyCs( object sender, CollectionChangedEventArgs args ) => RaisePropertyChanged(nameof(Cs));