【问题标题】:WPF unexpected ICollectionView behaviorWPF 意外的 ICollectionView 行为
【发布时间】:2021-07-01 00:36:20
【问题描述】:

我是 WPF 的新手,只是在学习它是如何工作的。我的最终目标是为每个标题创建一个数据网格,显示与每个类别相关的项目集合的总数。

但我看到了一些意想不到的行为。我希望看到这个输出:

标题1
类别1
类别2

标题2
第三类

我实际看到的是这样的:

标题1
第三类

标题2
第三类

我不知道为什么。希望有人可以阐明一点。感谢您的帮助!

这是我的代码:

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

【问题讨论】:

    标签: c# wpf icollectionview


    【解决方案1】:

    问题是,CollectionViewSource.GetDefaultView(categories) 将针对相同的输入返回相同的 ICollectionView 实例,无论您调用它的频率如何。

    由于您将相同的categories 传递给两个HeadingViewModel 构造函数,第二个构造函数this.Categories.Filter = CategoriesFilter 将覆盖两个标题的过滤器。

    【讨论】:

      【解决方案2】:

      grek40 因告诉我问题出在哪里而获得金星 - 但对于其他偶然发现此问题的人来说,这是解决方案:

      在 HeadingViewModel.cs 中更改这一行:

      this.Categories = CollectionViewSource.GetDefaultView(categories);
      

      到这里:

      this.Categories = new CollectionViewSource { Source = categories }.View;
      

      这会为解决问题的每个 Heading 实例返回一个新的视图源实例。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-16
        • 2011-01-09
        • 1970-01-01
        • 2011-09-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多