【问题标题】:C# DependencyProperty of generic class in UserControlUserControl中泛型类的C#DependencyProperty
【发布时间】:2022-01-07 11:03:48
【问题描述】:

我已经阅读了很多问题和答案来解决这个问题,但没有找到任何有助于解决这个问题的东西。我试图公开一个最小的例子,所以这不是我真正的代码(如果其中有任何错误,请告诉我)。

我想做什么?我只是想创建一个使用泛型类的 UserControl。我想像 WPF 框架一样使用它,我的意思是,我希望能够将属性绑定到任何类的通用对象(在 WPF 框架案例中为Itemssource,您可以将其绑定到ObservableCollection<AnyClass>)。

首先我定义我的类:

public class MyGenericClass<T> where T : IMyItemInterface
{ 
    public ObservableCollection<T> MyOriginalList { get; set; }
    public T MySelectedItem { get; set; }
    ... more code here ...
}

然后我创建一个包含此 DependencyProperty 的 UserControl (MyUserControl):

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&lt;IMyItemInterface&gt;) 更改为typeof(object)

public static readonly DependencyProperty ItemsListProperty =
    DependencyProperty.Register("ItemsList", typeof(object),
    typeof(MyUserControl),
    new FrameworkPropertyMetadata(new PropertyChangedCallback(OnListaItemsChanged)));

现在它确实触发了OnItemsListChanged,但现在我在get =&gt; (MyGenericClass&lt;IMyItemInterface&gt;)GetValue(ItemsListProperty); 中有一个强制转换异常,这是合乎逻辑的,因为 MyGenericClass 派生自对象而不是来自 MyGenericClass 的对象。所以我也尝试将属性更改为:

public object ItemsList
{
    get => GetValue(ItemsListProperty); 
    set => SetValue(ItemsListProperty, value);
}

现在它再次编译并继续触发OnItemsListChanged,但现在我不能使用 ItemList 做任何事情,因为我不能引用ItemLists.MySelectedItem(例如),因为MySelectedItemobject 类中不存在。

问题是:什么是正确的方法?我做错了什么?

更新:

我绑定到ItemsList 的是MyGenericClass 的一个实例。例如,我有一个实现 IMyItemInterface (MyClassItem) 的类,在代码隐藏MyGenericClass&lt;MyClassItem&gt; myItem = new(); 中执行后,我以这种方式在 XAML 中绑定:

<MyCustomControl ItemsList={Binding myItem, Mode=TwoWay} />

【问题讨论】:

  • MyGenericClass 是否应该是 IEnumerable 或 ICollection 或 IList?比使用 IEnumerable/ICollection/IList 作为依赖属性的类型。还要说明为什么你的 UserControl 应该实现 INotifyPropertyChanged。这对依赖属性毫无意义。
  • @Clemens 你是对的,INotifyPropertyChanged 对依赖属性毫无意义,我编辑不是问题并将其删除。关于另一个问题,不,它不是 IEnumerable,但它具有 IEnumerable 内部的属性。我更新了这个问题。谢谢
  • 输出窗口中是否有任何绑定错误?
  • @Carlos:您将ItemsList 属性设置或绑定到什么?
  • @mm8 我已使用此信息更新了问题。谢谢

标签: c# wpf dependency-properties


【解决方案1】:

您不能将 MyGenericClass&lt;IMyItemInterface&gt; 类型的属性设置为 MyGenericClass&lt;MyClassItem&gt;,因为 MyGenericClass&lt;MyClassItem&gt; 不是 MyGenericClass&lt;IMyItemInterface&gt; 只是因为 MyClassItem 实现了 IMyItemInterface。 p>

这称为变体,C# 将变体类型参数限制为泛型接口和泛型委托类型。

例如,您可以将IEnumerable&lt;object&gt; 字段设置为List&lt;string&gt;,但您不能将List&lt;object&gt; 字段设置为List&lt;string&gt;,尽管List&lt;T&gt; 实现了IEnumerable&lt;T&gt;

这是您的问题,也是您的依赖属性未设置且未调用回调的原因。

请参阅此问题和答案以获取更多信息:

C# variance problem: Assigning List<Derived> as List<Base>

docs on covariance and contravariance 也应该有帮助。

【讨论】:

  • 阅读您的回答后,我得出结论,我必须学习更多(更多)......谢谢。但是现在我有一个(可能是愚蠢的)问题......如果MyGenericClass 实现了 IEnumerable 接口会有帮助吗?由于它是一个协变接口,它对铸造有帮助吗?谢谢
【解决方案2】:

像这样声明依赖属性类型可能就足够了:

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() };

【讨论】:

  • 是的,这将是一种可能的解决方案,但这不是通用类。我需要它是通用的,MyGenericCollection&lt;T&gt;(或您命名的MyItemsList&lt;T&gt;),泛型是我的问题出现的地方。我希望能够将MyGenericCollection&lt;MyClassItem&gt;MyGenericCollection&lt;MyClassItem2&gt; 的对象绑定到MyUserControl,就像我将MyGenericCollection&lt;MyClassItem&gt;MyGenericCollection&lt;MyClassItem2&gt; 绑定到ObservableCollection 的ItemsSource 一样,没有任何问题。谢谢
  • 但是你应该明白那是行不通的。 “我需要它是通用的”只有在您不考虑替代品时才是正确的。
  • 当然,但我仍然缺少一些东西。对不起。
  • 你完全正确。我终于明白了。谢谢
  • 也许这个链接对有同样问题的人有用:levelup.gitconnected.com/…
猜你喜欢
  • 2012-03-02
  • 2012-06-06
  • 1970-01-01
  • 1970-01-01
  • 2014-06-22
  • 2016-05-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多