使用通用基类保存对通用 ObservableCollections 的引用



这个问题已经让我忙碌了半天,我开始失去理智:

我正在使用 UI 逻辑项目的东西。有"父"项,可以包含其他项的可观察集合。(两者都继承自同一个 ItemBase,图片节点与节点,有点递归( 为了不必在每个"父"项类上重新创建观察者逻辑,我想将功能添加到名为 ItemBase 的公共基类中。这个想法是,父级可以只注册其 ObservableCollections,基类负责事件路由和所有内容。问题是,我似乎找不到一种方法来保存对这些 ObservableCollections 的引用(具有相同基类的不同类型的(,以便泛型的工作方式。

代码如下:

public abstract class ItemBase : ViewModelBase
{
private List<ObservableItemCollection<ItemBase>> _trackedChildItemsList = new List<ObservableItemCollection<ItemBase>>();
public event EventHandler<ItemPropertyChangedEventArgs> ChildItemPropertyChanged;
public event EventHandler<IsDirtyChangedEventArgs> ChildItemIsDirtyChanged;
public override bool IsDirty
{
get { return base.IsDirty || AreAnyChildItemsDirty; }
set { base.IsDirty = value; }
}
private bool AreAnyChildItemsDirty
{
get
{
return _trackedChildItemsList.Any(i => i.Any(l => l.IsDirty));
}
}
protected void RegisterItemCollection<T>(ObservableItemCollection<T> collection)
where T : ItemBase
{
_trackedChildItemsList.Add(collection); // intellisense underlines 'collection'; cannot convert from 'ObservableItemCollection<T>' to ObservableItemCollection<ItemBase>:
collection.ItemPropertyChanged += Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged += Collection_ItemIsDirtyChanged;
}
public override void Dispose()
{
foreach (ObservableItemCollection<ItemBase> collection in _trackedChildItemsList)
{
collection.ItemPropertyChanged -= Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged -= Collection_ItemIsDirtyChanged;
}
base.Dispose();
}
private void Collection_ItemPropertyChanged(object sender, ItemPropertyChangedEventArgs e)
{
OnChildItemPropertyChanged(e);
}
protected virtual void OnChildItemPropertyChanged(ItemPropertyChangedEventArgs e)
{
ChildItemPropertyChanged?.Invoke(this, e);
}
private void Collection_ItemIsDirtyChanged(object sender, IsDirtyChangedEventArgs e)
{
OnItemIsDirtyChanged(e);
}
protected virtual void OnItemIsDirtyChanged(IsDirtyChangedEventArgs e)
{
ChildItemIsDirtyChanged?.Invoke(this, e);
}
}

如您所见,我使用的是 ObservableCollection 的派生自定义类型,即 ObservableItemCollection,它负责集合本身的 ItemPropertyChanged 和 ItemIsDirtyChanged 调用。这允许人们从外部捕捉这些事件。 现在,我希望它位于一个集中的位置,即基类,而不是在每个父项本身(重复(中"捕获事件"逻辑。

现在的主要问题是,在注册 ObservableItemCollections 时,我不可能保留对它们的引用,因为没有共同的基础。ObservableItemCollection<CustomItem>不会继承自ObservableItemCollection<ItemBase>,因为它是一个集合。我尝试用泛型解决整个问题,但是,以上是我所得到的。它无法编译我写"无法从'可观察项目集合'转换为可观察项目集合"注释的地方。

理解为什么它无法编译,但是,我似乎找不到解决方法/工作解决方案。

我绝对需要直接引用集合(转换为我的自定义类型 ObservableItemCollection(,否则整个事情将无法工作。您可以在代码中看到我正在访问集合本身的事件以及 ItemBase 的属性。

无论哪种方式,我似乎都找不到收藏的共同基础。我尝试使用基于动态和反射的转换,接口,自定义通用ParentItem类型,两者都不起作用(我可能忽略了一些东西(,即使它起作用,它也相当丑陋。

用有限的黑客攻击真的不可能实现我想要的东西吗?我不敢相信,在我投入了这么多时间之后,我没有找到一个好的解决方案。

附加信息:

在父项中,我有以下可观察集合:

public ObservableItemCollection<SomeItem1> Collection1 { get; set; } = new ObservableItemCollection<SomeItem1>();
public ObservableItemCollection<SomeItem2> Collection2 { get; set; } = new ObservableItemCollection<SomeItem2>();

其中两种项类型都继承自 ItemBase。然后我在父项构造函数中调用基方法RegisterItemCollection,如下所示:

RegisterItemCollection(Collection1);
RegisterItemCollection(Collection2);

WPF 集合控件具有相同的问题:如何定义可以保存对任何类型的泛型集合的引用的属性?答:使该属性成为对所有集合实现的非泛型接口的引用。这是一个非常普遍的问题,这就是为什么在引入泛型之后的这些年里,非泛型System.Collections.IEnumerableSystem.Collections.IList在整个 .NET 框架中仍然大量使用的原因。

您在RegisterItemCollection()IsDirtyDispose()中所做的任何事情都不需要关心集合中的项目类型。因此,请采用您需要与该代码交互的任何方法和属性,并将其全部放在非泛型接口或基类中。你的基类已经是泛型的(ObservableCollection<T>,我猜(,所以使用接口。

public interface IObservableItemCollection
{
event EventHandler<ItemPropertyChangedEventArgs> ItemPropertyChanged;
event EventHandler<IsDirtyChangedEventArgs> ItemIsDirtyChanged;
bool IsDirty { get; }
}
public interface IDirtyable
{
//  I'm pretty sure you'll want this event here, and I think you'll want your collection to 
//  implement IDirtyable too.
//event EventHandler<IsDirtyChangedEventArgs> IsDirtyChanged;
bool IsDirty { get; }
}
public class ObservableItemCollection<T>
: ObservableCollection<T>, IObservableItemCollection
where T : IDirtyable
{
public bool IsDirty => this.Any(item => item.IsDirty);
public event EventHandler<ItemPropertyChangedEventArgs> ItemPropertyChanged;
public event EventHandler<IsDirtyChangedEventArgs> ItemIsDirtyChanged;
}
public class ViewModelBase : IDisposable, IDirtyable
{
public virtual bool IsDirty => true;
public virtual void Dispose()
{
}
}
public class ItemBase : ViewModelBase
{
private List<IObservableItemCollection> _trackedChildItemsList = new List<IObservableItemCollection>();
public override bool IsDirty
{
get
{
return base.IsDirty || _trackedChildItemsList.Any(coll => coll.IsDirty);
}
}
protected void RegisterItemCollection<T>(ObservableItemCollection<T> collection)
where T : ItemBase
{
_trackedChildItemsList.Add(collection);
collection.ItemPropertyChanged += Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged += Collection_ItemIsDirtyChanged;
}
public override void Dispose()
{
foreach (IObservableItemCollection collection in _trackedChildItemsList)
{
collection.ItemPropertyChanged -= Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged -= Collection_ItemIsDirtyChanged;
}
base.Dispose();
}
private void Collection_ItemIsDirtyChanged(object sender, IsDirtyChangedEventArgs e)
{
}
private void Collection_ItemPropertyChanged(object sender, ItemPropertyChangedEventArgs e)
{
}
}
public class ItemPropertyChangedEventArgs : EventArgs
{
}
public class IsDirtyChangedEventArgs : EventArgs
{
}

你也可以通过使_trackedChildItemsList成为IDisposable的集合来做到这一点,并让集合清除自己的事件处理程序,但是一个类清除自己的事件处理程序是非常可怕的。当传统的OOP可用于以可读和可维护的方式完成工作时,避免反思。而且你仍然需要为IsDirty想点什么.

你不能这样做,因为如果可以的话,你可以做类似的事情

class A {}
class B : A { }
class C : A { }
var list = new List<List<A>>();
var sublist_b = new List<B>();
sublist_b.Add(new B());
list.Add(sublist_b);
var sublist = list.Single();
sublist.Add(new C()); // <- now a List<B> contains an object that ist not if type B or derived B

我建议你只用ObservableItemCollection<ItemBase>来固定你的对象。

相关内容

  • 没有找到相关文章

最新更新