UserControl 中泛型 class 的 C# DependencyProperty
C# DependencyProperty of generic class in UserControl
我已经阅读了很多问题和答案来解决这个问题,但没有找到任何有助于解决这个问题的方法。我试图公开一个最小的例子,所以这不是我的真实代码(如果有任何错误,请告诉我)。
我想做什么?我只是想创建一个使用通用 class 的 UserControl。我想像 WPF 框架一样使用它,我的意思是,我希望能够将 属性 绑定到任何 class 的通用对象(在 WPF 框架情况下为 Itemssource
,你可以将它绑定到 ObservableCollection<AnyClass>
).
首先我定义我的 class:
public class MyGenericClass<T> where T : IMyItemInterface
{
public ObservableCollection<T> MyOriginalList { get; set; }
public T MySelectedItem { get; set; }
... more code here ...
}
然后我创建一个 UserControl (MyUserControl
),其中包含此 DependencyProperty:
public partial class MyUserControl: UserControl
{
... code here ...
public static readonly DependencyProperty ItemsListProperty =
DependencyProperty.Register("ItemsList", typeof(MyGenericClass<IMyItemInterface>),
typeof(MyUserControl),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnItemsListChanged)));
private static void OnItemsListChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyUserControl myControl = (MyUserControl)d; // just for debugging
}
public MyGenericClass<IMyItemInterface> ItemsList
{
get => (MyGenericClass<IMyItemInterface>)GetValue(ItemsListProperty);
set => SetValue(ItemsListProperty, value);
}
... more code here ...
}
此代码可以编译,但无法运行。如果我在 OnItemsListChanged
中设置断点,它永远不会触发。
我可以这样把 typeof(MyGenericClass<IMyItemInterface>)
改成 typeof(object)
:
public static readonly DependencyProperty ItemsListProperty =
DependencyProperty.Register("ItemsList", typeof(object),
typeof(MyUserControl),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnListaItemsChanged)));
现在它确实触发了 OnItemsListChanged
但现在我在 get => (MyGenericClass<IMyItemInterface>)GetValue(ItemsListProperty);
中有一个转换异常,这是合乎逻辑的,因为 MyGenericClass 派生自对象而不是派生自 MyGenericClass 的对象。所以我也尝试将 属性 更改为:
public object ItemsList
{
get => GetValue(ItemsListProperty);
set => SetValue(ItemsListProperty, value);
}
现在它再次编译并继续触发 OnItemsListChanged
但现在我无法将 ItemList 用于任何事情,因为我无法引用 ItemLists.MySelectedItem
(例如),因为 MySelectedItem
不存在于object
class.
问题是:正确的方法是什么?我做错了什么?
更新:
我绑定到 ItemsList
的是 MyGenericClass 的一个实例。例如,我有一个实现 IMyItemInterface (MyClassItem
) 的 class,在代码隐藏 MyGenericClass<MyClassItem> myItem = new();
中完成后,我以这种方式绑定 XAML:
<MyCustomControl ItemsList={Binding myItem, Mode=TwoWay} />
您不能将 MyGenericClass<IMyItemInterface>
类型的 属性 设置为 MyGenericClass<MyClassItem>
,因为 MyGenericClass<MyClassItem>
是 而不是 MyGenericClass<IMyItemInterface>
只是因为 MyClassItem
实现了 IMyItemInterface
.
这称为变体,C# 将变体类型参数限制为泛型接口和泛型委托类型。
例如,您可以将 IEnumerable<object>
字段设置为 List<string>
,但不能将 List<object>
字段设置为 List<string>
,尽管 [=20] =] 实现 IEnumerable<T>
.
这是您的问题,也是您未设置依赖项 属性 且未调用回调的原因。
请参阅此问题和答案以获取更多信息:
C# variance problem: Assigning List<Derived> as List<Base>
像这样声明依赖项 属性 类型可能就足够了:
public class MyItemsList
{
public ObservableCollection<IMyItemInterface> MyOriginalList { get; set; }
public IMyItemInterface MySelectedItem { get; set; }
}
和依赖项 属性 像这样:
public MyItemsList ItemsList
{
get { return (MyItemsList)GetValue(ItemsListProperty); }
set { SetValue(ItemsListProperty, value); }
}
public static readonly DependencyProperty ItemsListProperty =
DependencyProperty.Register(
nameof(ItemsList), typeof(MyItemsList), typeof(MyUserControl));
像这样的赋值(和等效的绑定)现在可以工作了:
c.ItemsList = new MyItemsList { MySelectedItem = new MyClassItem() };
我已经阅读了很多问题和答案来解决这个问题,但没有找到任何有助于解决这个问题的方法。我试图公开一个最小的例子,所以这不是我的真实代码(如果有任何错误,请告诉我)。
我想做什么?我只是想创建一个使用通用 class 的 UserControl。我想像 WPF 框架一样使用它,我的意思是,我希望能够将 属性 绑定到任何 class 的通用对象(在 WPF 框架情况下为 Itemssource
,你可以将它绑定到 ObservableCollection<AnyClass>
).
首先我定义我的 class:
public class MyGenericClass<T> where T : IMyItemInterface
{
public ObservableCollection<T> MyOriginalList { get; set; }
public T MySelectedItem { get; set; }
... more code here ...
}
然后我创建一个 UserControl (MyUserControl
),其中包含此 DependencyProperty:
public partial class MyUserControl: UserControl
{
... code here ...
public static readonly DependencyProperty ItemsListProperty =
DependencyProperty.Register("ItemsList", typeof(MyGenericClass<IMyItemInterface>),
typeof(MyUserControl),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnItemsListChanged)));
private static void OnItemsListChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyUserControl myControl = (MyUserControl)d; // just for debugging
}
public MyGenericClass<IMyItemInterface> ItemsList
{
get => (MyGenericClass<IMyItemInterface>)GetValue(ItemsListProperty);
set => SetValue(ItemsListProperty, value);
}
... more code here ...
}
此代码可以编译,但无法运行。如果我在 OnItemsListChanged
中设置断点,它永远不会触发。
我可以这样把 typeof(MyGenericClass<IMyItemInterface>)
改成 typeof(object)
:
public static readonly DependencyProperty ItemsListProperty =
DependencyProperty.Register("ItemsList", typeof(object),
typeof(MyUserControl),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnListaItemsChanged)));
现在它确实触发了 OnItemsListChanged
但现在我在 get => (MyGenericClass<IMyItemInterface>)GetValue(ItemsListProperty);
中有一个转换异常,这是合乎逻辑的,因为 MyGenericClass 派生自对象而不是派生自 MyGenericClass 的对象。所以我也尝试将 属性 更改为:
public object ItemsList
{
get => GetValue(ItemsListProperty);
set => SetValue(ItemsListProperty, value);
}
现在它再次编译并继续触发 OnItemsListChanged
但现在我无法将 ItemList 用于任何事情,因为我无法引用 ItemLists.MySelectedItem
(例如),因为 MySelectedItem
不存在于object
class.
问题是:正确的方法是什么?我做错了什么?
更新:
我绑定到 ItemsList
的是 MyGenericClass 的一个实例。例如,我有一个实现 IMyItemInterface (MyClassItem
) 的 class,在代码隐藏 MyGenericClass<MyClassItem> myItem = new();
中完成后,我以这种方式绑定 XAML:
<MyCustomControl ItemsList={Binding myItem, Mode=TwoWay} />
您不能将 MyGenericClass<IMyItemInterface>
类型的 属性 设置为 MyGenericClass<MyClassItem>
,因为 MyGenericClass<MyClassItem>
是 而不是 MyGenericClass<IMyItemInterface>
只是因为 MyClassItem
实现了 IMyItemInterface
.
这称为变体,C# 将变体类型参数限制为泛型接口和泛型委托类型。
例如,您可以将 IEnumerable<object>
字段设置为 List<string>
,但不能将 List<object>
字段设置为 List<string>
,尽管 [=20] =] 实现 IEnumerable<T>
.
这是您的问题,也是您未设置依赖项 属性 且未调用回调的原因。
请参阅此问题和答案以获取更多信息:
C# variance problem: Assigning List<Derived> as List<Base>
像这样声明依赖项 属性 类型可能就足够了:
public class MyItemsList
{
public ObservableCollection<IMyItemInterface> MyOriginalList { get; set; }
public IMyItemInterface MySelectedItem { get; set; }
}
和依赖项 属性 像这样:
public MyItemsList ItemsList
{
get { return (MyItemsList)GetValue(ItemsListProperty); }
set { SetValue(ItemsListProperty, value); }
}
public static readonly DependencyProperty ItemsListProperty =
DependencyProperty.Register(
nameof(ItemsList), typeof(MyItemsList), typeof(MyUserControl));
像这样的赋值(和等效的绑定)现在可以工作了:
c.ItemsList = new MyItemsList { MySelectedItem = new MyClassItem() };