WPF 意外的 ICollectionView 行为
WPF unexpected ICollectionView behavior
我是 WPF 的新手,只是在学习其工作原理。我的最终目标是为每个标题创建一个数据网格,显示与每个类别相关的 collection 项的总计。
但我看到了一些意外行为。我希望看到这个输出:
标题 1
类别 1
类别 2
标题 2
类别 3
我实际看到的是这样的:
标题 1
类别 3
标题 2
类别 3
我不知道为什么。希望有人能阐明一点。感谢您的帮助!
这是我的代码:
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));
}
}
问题是,CollectionViewSource.GetDefaultView(categories)
将为相同的输入 return 相同的 ICollectionView
实例,无论您调用它的频率如何。
由于您将相同的 categories
传递给两个 HeadingViewModel
构造函数,第二个构造函数 this.Categories.Filter = CategoriesFilter
将覆盖两个标题的过滤器。
grek40 因告诉我问题出在哪里而获得金星 - 但这里是为其他遇到此问题的人提供的解决方案:
在 HeadingViewModel.cs 中更改此行:
this.Categories = CollectionViewSource.GetDefaultView(categories);
对此:
this.Categories = new CollectionViewSource { Source = categories }.View;
returns 解决问题的每个标题实例的视图源的新实例。
我是 WPF 的新手,只是在学习其工作原理。我的最终目标是为每个标题创建一个数据网格,显示与每个类别相关的 collection 项的总计。
但我看到了一些意外行为。我希望看到这个输出:
标题 1
类别 1
类别 2
标题 2
类别 3
我实际看到的是这样的:
标题 1
类别 3
标题 2
类别 3
我不知道为什么。希望有人能阐明一点。感谢您的帮助!
这是我的代码:
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));
}
}
问题是,CollectionViewSource.GetDefaultView(categories)
将为相同的输入 return 相同的 ICollectionView
实例,无论您调用它的频率如何。
由于您将相同的 categories
传递给两个 HeadingViewModel
构造函数,第二个构造函数 this.Categories.Filter = CategoriesFilter
将覆盖两个标题的过滤器。
grek40 因告诉我问题出在哪里而获得金星 - 但这里是为其他遇到此问题的人提供的解决方案:
在 HeadingViewModel.cs 中更改此行:
this.Categories = CollectionViewSource.GetDefaultView(categories);
对此:
this.Categories = new CollectionViewSource { Source = categories }.View;
returns 解决问题的每个标题实例的视图源的新实例。