简体   繁体   English

WPF附加属性无法为集合指定更改的回调

[英]WPF Attached Property unable to specify Changed Callback for collection

EDIT: In order to clear up all confusions with instanct-closing as duplicates. 编辑:为了消除与即时关闭重复的所有混乱。 Please see point (3.) explaining why the accepted answer does not apply. 请参阅第(3.)点,解释为什么不接受接受的答案。 In short, the linked answer is fine as long as you are not using XAML to set the value because XAML will never call PropertyChangedCallback because it re-uses the default instance. 简而言之,只要您不使用XAML来设置值,链接的答案就可以了,因为XAML 永远不会调用PropertyChangedCallback因为它会重用默认实例。


Question: 题:
Considering a simple WPF's Attached Property of ObservableCollection<T> type with XAML-defined value: 考虑具有XAML定义值的简单WPF的ObservableCollection<T>类型的Attached属性

// 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>

How can I hook collection changed events with access to the both the DependencyObject it is attached to and the new items? 如何通过访问附加到它的DependencyObject和新项来挂钩更改集合的事件? MyCollection must be definable in XAML. MyCollection 必须在XAML中定义。
It seems simple at first, but none of following worked for me: 乍一看似乎很简单,但以下任何一项对我都不起作用:

  1. Set callback new UIPropertyMetadata(null, CollectionChanged) causes crash: 设置回调new UIPropertyMetadata(null, CollectionChanged)会导致崩溃:

XamlObjectWriterException: 'Collection property 'System.Windows.Controls.Grid'.'MyCollection' is null.' XamlObjectWriterException:'集合属性'System.Windows.Controls.Grid'。'MyCollection'为null。

  1. OK, let's provide default value in order to avoid the crash above: new UIPropertyMetadata(new ObservableCollection<int>(), CollectionChanged) That, however prevent CollectionChanged from ever firing due to XAML not instantiating new collection but rather adding items to existing collection. 好的,让我们提供默认值以避免上面的崩溃: new UIPropertyMetadata(new ObservableCollection<int>(), CollectionChanged)但是,由于XAML不会实例化新集合而是向现有集合添加项目,因此阻止了CollectionChanged的触发。

  2. Fixing the above and hook CollectionChanged while providing default value new UIPropertyMetadata(ProvideWithRegisteredCollectionChanged(), CollectionChanged) does not work neither because there is no way to pass the DependencyProperty to ProvideWithRegisteredCollectionChanged() method due to being in static context. 解决上述问题并在提供默认值new UIPropertyMetadata(ProvideWithRegisteredCollectionChanged(), CollectionChanged)同时挂钩CollectionChanged也不起作用,因为由于处于静态上下文中而无法将DependencyProperty传递给ProvideWithRegisteredCollectionChanged()方法。

  3. Coalescing in MyCollection either in GetMyCollection() getter or CoerceValueCallback does not prevent the crash from point 1. above since it does not seem to be called before property is first accessed. 聚结在MyCollection无论是在GetMyCollection()吸气剂或CoerceValueCallback不会阻止从点1碰撞以上,因为它似乎没有被调用第一次被访问属性之前。

You can't correctly assign a non-null default value for a collection-type attached property. 您不能为集合类型附加属性正确分配一个非空的默认值。 Hence you have to create an instance in XAML. 因此,您必须在XAML中创建一个实例。

Since declaring an ObservableCollection directly in XAML seems not easily possible, declare an appropriate derived type: 由于在XAML中直接声明ObservableCollection似乎不太容易,因此请声明适当的派生类型:

public class MyCollection : ObservableCollection<int>
{
}

and create an instance in XAML like this: 并在XAML中创建一个实例,如下所示:

<Grid>
    <b:MyCollectionExtension.MyCollection>
        <b:MyCollection>
            <sys:Int32>1</sys:Int32>
            <sys:Int32>2</sys:Int32>
        </b:MyCollection>
    </b:MyCollectionExtension.MyCollection>
</Grid>

The attached property declaration should look like shown below, including the code that attaches and detaches a CollectionChanged event handler. 附加的属性声明应如下所示,包括附加和分离CollectionChanged事件处理程序的代码。

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)
        {
            // ...
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM