I'm new to WPF, and just learning how things work. My endgoal is to have a datagrid for each heading which displays totals from a collection of items associated with each category.
But I'm seeing some unexpected behavior. I expect to see this output:
Heading1
Category1
Category2
Heading2
Category3
What I actually see is this:
Heading1
Category3
Heading2
Category3
I'm stumped as to why. Hoping somebody could shed a little light. Thanks for any help!
Here's my code:
MainWindow.xaml
<Window x:Class="TestApp.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:TestApp"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<ItemsControl ItemsSource="{Binding Headings}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Description}" />
<DataGrid ItemsSource="{Binding Categories}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="" Binding="{Binding Description}" Width="*" />
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public ObservableCollection<HeadingViewModel> Headings { get; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
var category1 = new CategoryViewModel()
{
Id = 1,
Description = "Category 1",
HeadingId = 1,
};
var category2 = new CategoryViewModel()
{
Id = 2,
Description = "Category 2",
HeadingId = 1,
};
var category3 = new CategoryViewModel()
{
Id = 3,
Description = "Category 3",
HeadingId = 2,
};
var categories = new ObservableCollection<CategoryViewModel>();
categories.Add(category1);
categories.Add(category2);
categories.Add(category3);
var heading1 = new HeadingViewModel(categories)
{
Id = 1,
Description = "Heading 1",
};
var heading2 = new HeadingViewModel(categories)
{
Id = 2,
Description = "Heading 2",
};
this.Headings = new ObservableCollection<HeadingViewModel>();
this.Headings.Add(heading1);
this.Headings.Add(heading2);
}
HeadingViewModel.cs
public class HeadingViewModel : INotifyPropertyChanged
{
public ICollectionView Categories { get; }
public HeadingViewModel(ObservableCollection<CategoryViewModel> categories)
{
this.Categories = CollectionViewSource.GetDefaultView(categories);
this.Categories.Filter = CategoriesFilter;
}
public event PropertyChangedEventHandler PropertyChanged;
private bool CategoriesFilter(object item)
{
var category = item as CategoryViewModel;
return category.HeadingId == this.Id;
}
private int id;
public int Id
{
get
{
return id;
}
set
{
this.id = value;
OnPropertyChanged();
Categories.Refresh();
}
}
public string Description { get; set; }
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
CategoryViewModel.cs
public class CategoryViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public CategoryViewModel() {}
public int Id { get; set; }
public string Description { get; set; }
private int? headingId;
public int? HeadingId
{
get
{
return headingId;
}
set
{
this.headingId = value;
OnPropertyChanged();
}
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}