FreezableCollection 在子属性更改时不提供更改通知

Cam*_*ers 1 c# collections wpf notifications

我有一个 FreezableCollection,我想监视其子属性的更改。这是代码的一小部分:

public class FieldHeading : DependencyObject
{
    public static readonly DependencyProperty LayoutProperty = DependencyProperty.Register("Layout", typeof(FieldHeadingLayout), typeof(FieldHeading),
        new FrameworkPropertyMetadata(FieldHeadingLayout.Above,
        FrameworkPropertyMetadataOptions.AffectsRender |
        FrameworkPropertyMetadataOptions.AffectsMeasure |
        FrameworkPropertyMetadataOptions.AffectsParentMeasure));

    public FieldHeadingLayout Layout
    {
        get { return (FieldHeadingLayout) GetValue(LayoutProperty); }
        set { SetValue(LayoutProperty, value); }
    }

}

public class FieldPanel : FrameworkElement
{
    private static readonly DependencyProperty FieldHeadingProperty = DependencyProperty.Register("FieldHeading", typeof(FreezableCollection<FieldHeading>), typeof(FieldPanel),
        new FrameworkPropertyMetadata(null,
            FrameworkPropertyMetadataOptions.AffectsMeasure |
            FrameworkPropertyMetadataOptions.AffectsParentMeasure |
            FrameworkPropertyMetadataOptions.AffectsRender, HeadingChanged));

    private static void HeadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Debug.WriteLine("Hello");
    }

    public FreezableCollection<FieldHeading> FieldHeadings
    {
        get
        { return (FreezableCollection<FieldHeading>) GetValue(FieldHeadingProperty); }
        set { SetValue(FieldHeadingProperty, value);}
    }

    public FieldPanel()
    {
        AddVisual(_contentVisual = new DrawingVisual());
        FieldHeadings = new FreezableCollection<FieldHeading>();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我们为其中一个 FieldHeadings 的 Layout 分配一个新值,不会生成任何更改通知。显然我错过了一些重要的事情。HeadingChanged 永远不会被调用。

有关 FreezableCollection 的 MSDN 帮助可在此处找到:FreezableCollection,其中指出:

事件更改... 当 Freezable 或其包含的对象被修改时发生。(继承自 Freezable。)

预先感谢您的任何帮助。

〜卡梅伦

Oll*_*lly 5

事实上,你可以做你想做的事。这正是FreezableCollection<T>存在的原因!您需要做的就是更改FieldHeading为派生自Freezable而不是DependencyObject对集合中的项目进行更改将给出相同的更改通知,就像整个项目已被替换一样。

这是一个非常有用但鲜为人知的 WPF 功能。

来自查尔斯·佩措尔德本人

当然,每当向集合添加或从集合中删除项目时,这些可冻结集合类都会发出更改通知,而且当集合中任何项目的依赖属性发生更改时也会发出更改通知。这是一个极其强大的机制。

这是一个演示如何使用的小示例FreezableCollection<>。我在 Visual Studio 中创建了一个新的 WPF 项目。以下是MainWindow.xaml.cs的 XAML及其背后的 C# 代码:

<Window x:Class="FreezableCollection.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:FreezableCollection"
        x:Name="Root"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <DataTemplate DataType="{x:Type local:MyFreezable}">
            <CheckBox IsChecked="{Binding IsNice}" Content="Check me!"/>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <ListBox ItemsSource="{Binding ElementName=Root, Path=MyFreezables}" />
    </Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)
using System.Windows;

namespace FreezableCollection
{
    public partial class MainWindow : Window
    {
        public static readonly DependencyProperty MyFreezablesProperty =
            DependencyProperty.Register("MyFreezables", typeof(MyFreezableCollection), typeof(MainWindow), new FrameworkPropertyMetadata(null, FreezablesChangedCallback));

        public MyFreezableCollection MyFreezables
        {
            get => (MyFreezableCollection)GetValue(MyFreezablesProperty);
            set => SetValue(MyFreezablesProperty, value);
        }

        public MainWindow()
        {
            InitializeComponent();
            MyFreezables = new MyFreezableCollection { new MyFreezable() };
        }

        private static void FreezablesChangedCallback(object sender, DependencyPropertyChangedEventArgs args)
        {
            MessageBox.Show("Changed!");
        }
    }

    public class MyFreezableCollection : FreezableCollection<MyFreezable>
    {
        protected override Freezable CreateInstanceCore() => new MyFreezableCollection();
    }

    public class MyFreezable : Freezable
    {
        public static readonly DependencyProperty IsNiceProperty =
            DependencyProperty.Register("IsNice", typeof(bool), typeof(MyFreezable), new PropertyMetadata(false));

        public bool IsNice
        {
            get => (bool)GetValue(IsNiceProperty);
            set => SetValue(IsNiceProperty, value);
        }

        protected override Freezable CreateInstanceCore() => new MyFreezable();
    }
}
Run Code Online (Sandbox Code Playgroud)

该示例显示一个包含单个CheckBox. 单击它可以切换IsNice数据绑定中项目的属性MyFreezableCollection。即使列表中没有添加或删除任何内容,也会调用依赖属性更改回调。