PropertyChanged 与 CollectionChanged - 请帮助理解基础知识

问题描述 投票:0回答:0

经过大量研究,我确实需要你的帮助。我的第一个版本的评论一直在帮助我寻找新的关键词。

在研究时我发现了几篇这样的文章:
CollectionChanged的信息
实现 CollectionChanged

关于 CollectionChanged 的不错的基本线程

但是,我理解和应用不正确。

问题:

  • 更改属性和更改其内容有什么区别?你能帮助我通过属性
    Activity
    SetOfCandidates
    来理解这一点吗?
  • 所列线程中的评论是关于
    BindingList
    ObservableCollection
    、性能、
    CollectionChanged
    调用
    PropertyChanged
    的赞成和反对。在这里设置我的项目时我需要记住什么?
  • CollectionChanged
    被提出后,我该怎么办?我无法让 UI 做我想做的事。
  • 如果 CollectionChanged 被订阅,何时以及如何取消订阅?

设置:

  • MVVM
  • 应用程序是关于解决数独(我的问题不是关于解决算法)
  • INotifyChanged
    在Model和ViewModel中实现
  • BindingList
    也被测试了;对于这个线程,我坚持
    ObservableCollection
  • CollectionChanged
    为物业实施
    SetOfCandidates
  • 转换器实现
  • 调试所有属性的内容没问题;存储正确的值

一个属性是一个存储某种活动的枚举。根据它的值,视图将更新背景颜色。 这工作正常.

另一个属性是存储一组候选人的整数列表。在运行时,候选集将减少,直到只剩下一个数字。我想在视图中显示这些更改。 这根本行不通.

Activity 属性运行良好。在初始化时,颜色没有设置,在运行时,颜色会根据 Activity 中存储的内容而改变。

    /// <summary>
    /// This <see cref="Enum"/> is used to define the activities, 
    /// how the algorithm is finding a candidate.
    /// </summary>
    public enum Activity
    {
        Clue,
        Singleton,
        Single,
        Twin,
        Triple,
        BruteForce,
        Initialization,
    }

    private Activity _Activity;
    /// <summary>
    /// This property stores the activity performed to manipulate the cell.
    /// This can be used in the View to make this activity visible 
    /// by using a converter changing the background color.
    /// </summary>
    public Activity Activity { get => _Activity; set => SetProperty(ref _Activity, value); }
    public class SudokuValueColorConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (Enum.IsDefined(value.GetType(), value) == false)
                return DependencyProperty.UnsetValue;
            var activity = (Activity)value;
            switch (activity)
            {
                case Activity.Clue:
                    return Brushes.Blue;
                case Activity.Singleton:
                    return Brushes.Orange;
                case Activity.Single:
                    return Brushes.Green;
                case Activity.Twin:
                    return Brushes.Brown;
                case Activity.Triple:
                    return Brushes.Yellow;
                case Activity.BruteForce:
                    return Brushes.Red;
                case Activity.Initialization:
                    return null;
                default:
                    return Brushes.Black;
            }
        }
    }

因此,我试图将其应用于物业

SetOfCandidates
。在第一次运行中,这个属性是一个
ArrayList
。这是行不通的,因为更改数组的成员不会引发
PropertyChanged
事件(如果我对此进行了适当的研究-经过更多研究,我会说没有像
ArrayList
BindingList
ObservableCollection这样的类型
正在引发此事件,如果仅更改内容)。建议是用
ArrayList
替换
ObservableCollection
,我这样做了,但它仍然没有像我期望的那样工作。在对线程发表第一条评论后,我正在查找
CollectionChanged
。调试了一下发现确实没有输入
SetOfCandidates
的setter,在编辑内容的时候。作为下一步,我将
CollectionChanged
事件添加到设置器中。在这个事件处理程序中,我放置了一个
Debug.Writeline()
,现在我看到了这个事件被引发的频率。


private ObservableCollection<int> _SetOfCandidates;
public ObservableCollection<int> SetOfCandidates
{
    get => _SetOfCandidates;
    set
    {
        Debug.WriteLine($"Passing Setter of {nameof(SetOfCandidates)}");
        SetProperty(ref _SetOfCandidates, value);
        SetOfCandidates.CollectionChanged += SetOfCandidates_CollectionChanged;
    }
}

private void SetOfCandidates_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    Debug.WriteLine($"Passing {nameof(SetOfCandidates_CollectionChanged)}");
    OnPropertyChanged(nameof(_SetOfCandidates));
}


    public class SudokuCandidateToContentConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string contentValue = string.Empty;
            var arrayList = (ObservableCollection<int>)value;
            if (arrayList == null) return "+"; //just for testing
            if (arrayList.Count == 0) return "++"; //just for testing
            //if (arrayList == null) return contentValue;
            //if (arrayList.Count == 0) return contentValue;
            foreach (var item in arrayList)
            {
                if (item == (int)parameter)
                {
                    contentValue = (string)parameter;
                }
            }
             return contentValue;
        }
    }

我的期望:

我希望在应用程序启动时在视图中显示一个空字符串。稍后,用户正在加载一个数独游戏,将线索放入视图中。在这种情况下,这些特定单元格的 SetOfCandidates 计数为零,标签内容为空字符串。所有其他未设置任何值的单元格都根据算法的逻辑更新它们的 SetOfCandidates。标签的内容应该使 SetOfCandidates 在视图中可见。

发生了什么事? 只有在启动应用程序时,才会调用更新标签内容的转换器。这与每当绑定属性发生变化时调用的 SudokuValueColorConverter 形成对比。

这是xaml代码。 Border 中的转换器工作正常。 Label 中的转换器只被调用一次。

    <Border Style="{StaticResource borderSudokuCandidatesCell}" Background="{Binding Sudoku[0].Activity, Converter={StaticResource sudokuValueColorConverter}}">
        <UniformGrid>
            <Label
               Content="{Binding Sudoku[0].SetOfCandidates, Converter={StaticResource sudokuCandidateToContentConverter}, ConverterParameter=1}" 
               Style="{StaticResource labelSudokuCandidates}">
            </Label>
            <Label
    <!-- *** repeated with changing ConverterParameter from 1 to 9 ***-->
            <Label
               Content="{Binding Sudoku[0].SetOfCandidates, Converter={StaticResource sudokuCandidateToContentConverter}, ConverterParameter=9}" 
               Style="{StaticResource labelSudokuCandidates}">
            </Label>
        </UniformGrid>
    </Border>

这里有一些图片。

  • ImageA:应用程序在起点。正如预期的那样,标签的内容是 ++(用于测试的占位符)。
  • ImageB:加载数独之后。预期的是,一个标签显示内容 8,所有其他标签应该是一个空字符串。
  • ImageC:解决数独之后。预期的是,所有标签都应该是一个空字符串。

ImageA

ImageB

ImageC

亲爱的社区,如果您对我有任何建议,请帮助我。感谢您的意见。如果你看到一个更聪明的方法来实现我的想法,请告诉我。

c# mvvm observablecollection inotifypropertychanged
© www.soinside.com 2019 - 2024. All rights reserved.