C# WPF Attached属性无法为集合指定已更改的回调

C# WPF Attached属性无法为集合指定已更改的回调,c#,wpf,xaml,attached-properties,C#,Wpf,Xaml,Attached Properties,编辑: 为了消除Instant closing作为重复项的所有混淆。请参见第(3)点,解释接受答案不适用的原因。简而言之,只要不使用XAML设置值,链接的答案就可以了,因为XAML将永远不会调用PropertyChangedCallback,因为它重新使用默认实例 问题: 考虑到带有XAML定义值的ObservableCollection类型的简单WPF附加属性: // public static class MyCollectionExetension.cs public static Ob

编辑: 为了消除Instant closing作为重复项的所有混淆。请参见第(3)点,解释接受答案不适用的原因。简而言之,只要不使用XAML设置值,链接的答案就可以了,因为XAML将永远不会调用
PropertyChangedCallback
,因为它重新使用默认实例


问题:
考虑到带有XAML定义值的
ObservableCollection
类型的简单WPF附加属性:

// public static class MyCollectionExetension.cs
public static ObservableCollection<int> GetMyCollection(DependencyObject obj)
{
    return (ObservableCollection<int>)obj.GetValue(MyCollectionProperty);
}

public static void SetMyCollection(DependencyObject obj, ObservableCollection<int> value)
{
    obj.SetValue(MyCollectionProperty, value);
}

public static readonly DependencyProperty MyCollectionProperty =
    DependencyProperty.RegisterAttached("MyCollection", typeof(ObservableCollection<int>), 
    typeof(MyCollectionExetension), new PropertyMetadata(null);

public static void DoThisWhenMyCollectionChanged(DependencyObejct assignee, IEnumerable<int> newValues) {
   // how can I invoke this?
}

//UserControl.xaml
<Grid xmlns:sys="clr-namespace:System;assembly=mscorlib">
    <b:DataGridExtensions.MyCollection >
        <sys:Int32>1</sys:Int32>
        <sys:Int32>2</sys:Int32>
    </b:DataGridExtensions.MyCollection>
</Grid>
//公共静态类MyCollectionExetension.cs
公共静态ObservableCollection GetMyCollection(DependencyObject obj)
{
返回(ObservableCollection)对象GetValue(MyCollectionProperty);
}
公共静态void SetMyCollection(DependencyObject对象,ObservableCollection值)
{
对象SetValue(MyCollectionProperty,value);
}
公共静态只读DependencyProperty MyCollectionProperty=
DependencyProperty.RegisterAttached(“MyCollection”,类型为(ObservableCollection),
typeof(MyCollectionExetension),新属性元数据(null);
public static void DoThisWhenMyCollectionChanged(DependencyObjCT受让人,IEnumerable newValue){
//我如何调用这个?
}
//UserControl.xaml
1.
2.
我怎样才能钩住集合更改事件,同时访问它所附加的
DependencyObject
和新项目?MyCollection必须在XAML中定义。
一开始似乎很简单,但以下几点对我都不起作用:

  • 设置回调
    新UIPropertyMetadata(null,CollectionChanged)
    导致崩溃:
  • XamlObjectWriterException:“集合属性”System.Windows.Controls.Grid.“'MyCollection'为空。”

  • 好的,让我们提供默认值以避免上面的崩溃:
    newUIPropertyMetadata(newObservableCollection(),CollectionChanged)
    但是,这会阻止
    CollectionChanged
    由于XAML没有实例化新集合,而是向现有集合添加项而触发

  • 当提供默认值时,修复上述问题并钩住
    CollectionChanged
    ,同时提供新的UIPropertyMetadata(ProvideWithRegisteredCollectionChanged(),CollectionChanged)也不起作用,因为无法将
    DependencyProperty
    传递给
    ProvideWithRegisteredCollectionChanged()
    方法,因为处于静态上下文中

  • MyCollection
    中合并
    GetMyCollection()
    getter或
    improverevaluecallback
    都不能防止第1点的崩溃。因为在首次访问属性之前似乎没有调用它

  • 无法为集合类型附加的属性正确分配非null默认值。因此,必须在XAML中创建实例

    由于直接在XAML中声明ObservableCollection似乎不太容易,请声明适当的派生类型:

    public class MyCollection : ObservableCollection<int>
    {
    }
    

    还要看一看:这无关紧要。重点是您必须在PropertyChangedCallback中附加CollectionChanged事件处理程序。这与dependency属性与attached属性无关。
    CollectionChanged
    回调(
    DependencyPropertyChangedCallback
    )仅当dependency属性获得分配的新集合时才会调用。在
    CollectionChanged
    回调中,您还必须订阅
    MyCollection.CollectionChanged
    事件才能在实际集合(而不仅仅是属性)发生时收到通知已更改。@Clemens抱歉,我的错误,但答案仍然不适用,请参见编辑-如果您在XAML中设置值,它将重新使用默认值,而不是实例化新的
    ObservaleCollection
    ,因此将不会首先调用
    属性ChangedCallback
    (并且答案只调用一次),我将永远不会给我机会注册
    NotifyCollectionChanged
    。或者,如果您仍然不信任我,则答案确实适用,请尝试提供的示例并应用答案(我尝试的第3点).Afaik无法安全地为集合类型附加的属性提供非空的默认值。您必须在XAML中执行显式集合实例创建:
    …elements…
    <Grid>
        <b:MyCollectionExtension.MyCollection>
            <b:MyCollection>
                <sys:Int32>1</sys:Int32>
                <sys:Int32>2</sys:Int32>
            </b:MyCollection>
        </b:MyCollectionExtension.MyCollection>
    </Grid>
    
    public static class MyCollectionExtension
    {
        public static MyCollection GetMyCollection(DependencyObject obj)
        {
            return (MyCollection)obj.GetValue(MyCollectionProperty);
        }
    
        public static void SetMyCollection(DependencyObject obj, MyCollection value)
        {
            obj.SetValue(MyCollectionProperty, value);
        }
    
        public static readonly DependencyProperty MyCollectionProperty =
            DependencyProperty.RegisterAttached(
                "MyCollection",
                typeof(MyCollection),
                typeof(MyCollectionExtension),
                new PropertyMetadata(MyCollectionPropertyChanged));
    
        public static void MyCollectionPropertyChanged(
            DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            var oldCollection = e.OldValue as MyCollection;
            var newCollection = e.NewValue as MyCollection;
    
            if (oldCollection != null)
            {
                oldCollection.CollectionChanged -= MyCollectionChanged;
            }
            if (newCollection != null)
            {
                newCollection.CollectionChanged += MyCollectionChanged;
            }
        }
    
        public static void MyCollectionChanged(
            object o, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                // ...
            }
        }
    }