简体   繁体   中英

WPF MVVM - Update Datagrid of the MainView from a child viewmodel

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;
    }

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; } public ChildViewModel childViewModel { get; set; } 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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM