WPF 附加 属性 无法指定集合的更改回调
WPF Attached Property unable to specify Changed Callback for collection
编辑:
为了消除与即时关闭重复的所有混淆。请参阅第 (3.) 点,解释为什么已接受的答案不适用。简而言之,只要您不使用 XAML 设置值,链接的答案就可以了,因为 XAML 将 never 调用 PropertyChangedCallback
因为它重新使用默认实例。
问题:
考虑具有 XAML 定义值的 ObservableCollection<T>
类型的简单 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>
我如何才能通过访问它所附加的 DependencyObject
和新项目来挂钩集合更改事件? MyCollection 必须 可在 XAML.
中定义
一开始看起来很简单,但是下面的 none 对我有用:
- 设置回调
new UIPropertyMetadata(null, CollectionChanged)
导致崩溃:
XamlObjectWriterException: 'Collection property 'System.Windows.Controls.Grid'.'MyCollection' is null.'
好的,让我们提供默认值以避免上面的崩溃:new UIPropertyMetadata(new ObservableCollection<int>(), CollectionChanged)
但是,由于 XAML 不是实例化新集合而是将项目添加到现有集合,因此可以防止 CollectionChanged
触发。
修复上述问题并在提供默认值 new UIPropertyMetadata(ProvideWithRegisteredCollectionChanged(), CollectionChanged)
的同时挂钩 CollectionChanged
也不起作用,因为无法将 DependencyProperty
传递给 ProvideWithRegisteredCollectionChanged()
方法,因为处于静态上下文中。
- 在
GetMyCollection()
getter 或 CoerceValueCallback
中合并 MyCollection
并不能防止上述第 1 点的崩溃,因为它似乎没有在 属性 最先访问。
您无法为附加的 collection-type 正确分配 non-null 默认值 属性。因此,您必须在 XAML.
中创建一个实例
由于直接在 XAML 中声明一个 ObservableCollection 似乎不太可能,因此声明一个适当的派生类型:
public class MyCollection : ObservableCollection<int>
{
}
并在 XAML 中创建一个实例,如下所示:
<Grid>
<b:MyCollectionExtension.MyCollection>
<b:MyCollection>
<sys:Int32>1</sys:Int32>
<sys:Int32>2</sys:Int32>
</b:MyCollection>
</b:MyCollectionExtension.MyCollection>
</Grid>
附加的 属性 声明应如下所示,包括附加和分离 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)
{
// ...
}
}
}
编辑:
为了消除与即时关闭重复的所有混淆。请参阅第 (3.) 点,解释为什么已接受的答案不适用。简而言之,只要您不使用 XAML 设置值,链接的答案就可以了,因为 XAML 将 never 调用 PropertyChangedCallback
因为它重新使用默认实例。
问题:
考虑具有 XAML 定义值的 ObservableCollection<T>
类型的简单 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>
我如何才能通过访问它所附加的 DependencyObject
和新项目来挂钩集合更改事件? MyCollection 必须 可在 XAML.
中定义
一开始看起来很简单,但是下面的 none 对我有用:
- 设置回调
new UIPropertyMetadata(null, CollectionChanged)
导致崩溃:
XamlObjectWriterException: 'Collection property 'System.Windows.Controls.Grid'.'MyCollection' is null.'
好的,让我们提供默认值以避免上面的崩溃:
new UIPropertyMetadata(new ObservableCollection<int>(), CollectionChanged)
但是,由于 XAML 不是实例化新集合而是将项目添加到现有集合,因此可以防止CollectionChanged
触发。修复上述问题并在提供默认值
new UIPropertyMetadata(ProvideWithRegisteredCollectionChanged(), CollectionChanged)
的同时挂钩CollectionChanged
也不起作用,因为无法将DependencyProperty
传递给ProvideWithRegisteredCollectionChanged()
方法,因为处于静态上下文中。- 在
GetMyCollection()
getter 或CoerceValueCallback
中合并MyCollection
并不能防止上述第 1 点的崩溃,因为它似乎没有在 属性 最先访问。
您无法为附加的 collection-type 正确分配 non-null 默认值 属性。因此,您必须在 XAML.
中创建一个实例由于直接在 XAML 中声明一个 ObservableCollection 似乎不太可能,因此声明一个适当的派生类型:
public class MyCollection : ObservableCollection<int>
{
}
并在 XAML 中创建一个实例,如下所示:
<Grid>
<b:MyCollectionExtension.MyCollection>
<b:MyCollection>
<sys:Int32>1</sys:Int32>
<sys:Int32>2</sys:Int32>
</b:MyCollection>
</b:MyCollectionExtension.MyCollection>
</Grid>
附加的 属性 声明应如下所示,包括附加和分离 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)
{
// ...
}
}
}