0

I have an application with a MainWindow that contains a ContentControl, inside of which I put a number of views. One of them contains a DataGrid that is supposed to show some data, which is bound to an ObservableCollection in the ViewModel. I am not able to make the binding work; if I initialize the data in the ViewModel constructor the data get displayed correctly, but if I load them dinamically after application start ("load data" button) the grid just doesn't get updated. Please help I've been busting my head on this for two days now. I already tried the fix suggested here: C#/WPF - View does not update, no known fix works but it doesn' work. On the contrary, it prevents data being loaded also from the ViewModel constructor.

Following is a simplified example just to show my issue.

This is my MainWindow:

<Window x:Class="TestINotifyProperty.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:local="clr-namespace:TestINotifyProperty"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:ViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="50"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        
        <ContentControl Grid.Row="1"
                        Grid.Column="1"
                        Margin="10"
                        Content="{Binding CurrentView}"/>
      
        <Button Grid.Column="1" Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Load Data" Command="{Binding LoadData}"/>
        <Button Grid.Column="1" Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="2,2,100,0" Content="Change View" Command="{Binding ChangeView}"/>
    </Grid>
</Window>

The View with the DataGrid:

<UserControl x:Class="TestINotifyProperty.DemoView"
             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:TestINotifyProperty" 
             d:DataContext="{d:DesignInstance Type=local:ViewModel}"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.DataContext>
        <local:ViewModel/>
    </UserControl.DataContext>
    <Grid>
        <StackPanel>
            <TextBlock HorizontalAlignment="Center" VerticalAlignment="Top" Text="Demo View" FontSize="100" FontFamily="courier" Foreground="Blue"/>
            <DataGrid ItemsSource="{Binding Persons}"/>
        </StackPanel>
    </Grid>
</UserControl>

My Model Class:

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace TestINotifyProperty
{
    internal class Model : ObservableObject
    {
        public class Person : INotifyPropertyChanged
        {
            private string surname;

            public string Surname
            {
                get { return surname; }
                set
                {
                    surname = value;
                    OnPropertyChanged();
                }
            }

            private string name;

            public string Name
            {
                get { return name; }
                set { name = value;
                    OnPropertyChanged(); 
                }
            }


            public event PropertyChangedEventHandler PropertyChanged;
            protected void OnPropertyChanged([CallerMemberName] string name = null)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
            }
        }
    }
}

My ViewModel:

using System.Collections.ObjectModel;

namespace TestINotifyProperty
{
    internal class ViewModel : ObservableObject
     
    {
        public ObservableCollection<Model.Person> persons { get; set; }
        public ObservableCollection<Model.Person> Persons { get { return persons; } }
        public RelayCommand LoadData { get; set; }
        public RelayCommand ChangeView { get; set; }

        public  HomeViewModel HomeVM {get; set;}    
        internal DemoViewModel DemoVM { get; set; }

        private object _currentView;
        public object CurrentView
        {
            get { return _currentView; }
            set
            {
                _currentView = value;
                OnPropertyChanged();
            }
        }

        public ViewModel()
        {
            LoadData = new RelayCommand(o => { Load(); });
            ChangeView = new RelayCommand(o => { changeView();});
            persons = new ObservableCollection<Model.Person>();
            HomeVM = new HomeViewModel();
            DemoVM = new DemoViewModel();
            CurrentView = HomeVM;
            Persons.Add(new Model.Person { Surname= "Doe", Name = "John" });
            Persons.Add(new Model.Person { Surname = "Doe", Name = "Jane" });
            Persons.Add(new Model.Person { Surname = "Doe", Name = "Baby" });
        }

        private void Load()
        {
            CurrentView = DemoVM;
            Persons.Add(new Model.Person { Surname = "Roe", Name = "Richard" });
            Persons.Add(new Model.Person { Surname = "Roe", Name = "Janie" });
        }
       private void changeView()
        {
            if (CurrentView == HomeVM)
                CurrentView = DemoVM;
            else CurrentView = HomeVM;
        }
    }
}

ObservableObject Class (just used to derive classes to implement INotifyPropertyChanged):

using System.ComponentModel;
using System.Runtime.CompilerServices;


namespace TestINotifyProperty
{
    public class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    }
}

DemoViewModel class:

namespace TestINotifyProperty
{
    public class DemoViewModel : ObservableObject
    {
    }
}

App.xaml file:

<Application x:Class="TestINotifyProperty.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:TestINotifyProperty"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <DataTemplate DataType="{x:Type local:HomeViewModel}">
            <local:HomeView/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:DemoViewModel}">
            <local:DemoView/>
        </DataTemplate>
    </Application.Resources>
</Application>
Yunnosch
  • 26,130
  • 9
  • 42
  • 54
FraCas
  • 1
  • 3
  • 1
    Please do not edit solution announcements into the question. Accept (i.e. click the "tick" next to it) one of the existing answer, if there are any. You can also create your own answer, and even accept it, if your solution is not yet covered by an existing answer. Compare https://stackoverflow.com/help/self-answer you might need to first get the question reopened. e.g. by explaining why the proposed duplicate does not help in your case. – Yunnosch Sep 06 '22 at 20:22
  • I couldn't do either thing because the question was closed. – FraCas Sep 07 '22 at 22:47
  • That is what the last part of my comment is about. – Yunnosch Sep 08 '22 at 05:45

0 Answers0