0
votes

I think it may be quite simple but I have been googling a solution for a while without success, and I am not sure what is the correct approach. I saw that Prism could be a solution but I am looking for something simple.

I have a MainViewModel and a MainView which contains a Datagrid. The MainView contains also a ContentControl which display a ChildView (with DataContext as ChildViewModel).

I use DataTemplate to associate Views and ViewModels.

The DataGrid of the MainView is Binded to an ObservableCollection of the ChildViewModel.

I want this DataGrid to be updated every time this Collection is modified. I have tried to use the INotifyPropertyChanged. I have tried to use the OnCollectionChanged.

When I debug I can see that the Collection has changed and that event is fired but how to refresh the binding ? (dataGrid is not updated).

Here is the code:

DataTemplate

<DataTemplate DataType="{x:Type vm:ChildViewModel}">
    <vi:ChildView />
</DataTemplate>

XAML MainView

     <ContentControl Content="{Binding childViewModel}"/>

     <DataGrid x:Name="DataGridChildren"
                       ItemsSource="{Binding childViewModel.Children,Mode=TwoWay}" 
                       SelectedItem="{Binding childViewModel.SelectedItem}" EnableRowVirtualization="True" IsReadOnly="True" AutoGeneratingColumn="DataGrid_AutoGeneratingColumn">

MainViewModel

public ChildViewModel childViewModel { get; set; }

public MainViewModel()
{
    childViewModel = new ChildViewModel();
}

ViewModelBase

public abstract class ViewModelBase : INotifyPropertyChanged
    {

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            var handler = PropertyChanged;
            handler?.Invoke(this, args);
        }
    }

ChildViewModel

 public class ChildViewModel: ViewModelBase
    {
        private ObservableCollection<Child> _Children;
        public ObservableCollection<Child> Children
        {
            get { return _Children; }
            set
            {
                _Children = value;
                OnPropertyChanged("Children");
                _Children.CollectionChanged +=handler;   
            }
        }

        private void handler(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            //What to do ?
        }

        public ChildViewModel()
        {
            Children = new ObservableCollection<Child>(ChildService.GetAllChild());
        }`    

Edit @lezhkin11

Yes I think that is the problem. But I have no idea how to fix it

Code behind MainView

void MainView_Loaded(object sender, RoutedEventArgs e)
{  
    mainViewModel = new MainViewModel();
    DataContext = mainViewModel;
}

Code behind ChildView

void ChildView_Loaded(object sender, RoutedEventArgs e)
{
     childViewModel = new ChildViewModel();
     DataContext = childViewModel;
}

A button allow to do a new search:

 private void BtnRefresf_Click(object sender, RoutedEventArgs e) // Search
 {
      childViewModel.Search();
 }

Then a method in the childViewModel will use the service to give a new value to the Observable collection (when I debug, I can reach the OnpropertyChanged of the collection)

   public void Search()
     {
            ObservableCollection<Child> collec = new ObservableCollection<Child>(ChildService.GetAllChild());

            Children = collec;
    }
1
Logic you are using is correct. I suspect problem is here <vi:ChildView />. You simply create view and another instance of view-model. So, your datagrid bound to one instance of ChildViewModel and ChildView to another. It's just an assumption. Could you provide code-behind of the ChildView, its XAML and place where you add new itemslezhkin11
Actually for now, I am trying to renew completely the collection. I have tried with Children.Add(..) I get the same result.Jlo
You have 2 options. 1) remove this method ChildView_Loaded and in DataTemplate do <vi:ChildView DataContext="{Binding}" />. 2) remove public ChildViewModel childViewModel { get; set; } then remove ContentControl and DataTemplate. Your new XAML <vi:ChildView x:Name="MyChild" /> <DataGrid ItemsSource="{Binding DataContext.Children, ElementName=MyChild,Mode=TwoWay}" lezhkin11
Thank you. the first option is not really convenient as I have several controls on the view which use the childviewmodel. I tried the second one, but I get the error : Cannot find source for binding with reference 'ElementName=MyChild'Jlo
It should work. Pretty simple scenario. Looks like you haven't assigned name to ChildView. Once again: 1) you remove ChildViewModel property from MainViewModel. 2) Then you remove ContentControl and DataTemplate in the XAML 3) Directly create ChildView in XAML and assign name to it x:Name="MyChild".lezhkin11

1 Answers

0
votes

Your DataGrid is bound to ChildViewModel inside MainViewModel (instance 1). While ChildView has it's own ChildViewModel (instance 2).

You have to make sure that both controls reference to the same instance.

1) Remove completely public ChildViewModel childViewModel { get; set; } from MainViewModel

2) Give a name to the ChildView.

<vi:ChildView x:Name="MyChild" />

3) Bind DataGrid's properties to correct view-model

 <DataGrid ItemsSource="{Binding DataContext.Children, ElementName=MyChild, Mode=TwoWay}" />

Also, I would recommend initializing DataContext directly in constructor. Loaded event is too late - as result you always have lot's of XAML binding exceptions that affect performance (even can lead to unexpected behavior)