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>