我有几个收藏,每个都有特定的类别。我需要每个实例(模型)都存储其父集合(以便能够通过数据验证访问其他实例)。试图找到一些聪明的解决方案 - 开始怀疑是否有这样的。
到目前为止,我想出了以下内容。
通用基类:
public class ModelBase<T> : ObservableObject, IDataErrorInfo
{
public CustomObservableCollection<T> ParentCollection;
public int Id { get; set; } // some generic prop
}
个人班(模特):
public class Model1: ModelBase<Model1>
{
public string Name { get; set; } // Some specific prop
// Data Error validation here, e.g. check of uniqueness
if (ParentCollection?.Where(c => c.Id != Id).Any(c => c.Name == Name) == true)
{
}
}
public class Model2: ModelBase<Model2>
{}
自定义 ObservableCollection:
public class CustomObservableCollection<T> : ObservableCollection<T> where T : ModelBase<T>
{
public CustomObservableCollection() : base() { }
public CustomObservableCollection(List<T> list)
: base(list)
{
foreach (T item in list)
{
item.ParentCollection = this;
}
}
public void AddEdit(T item)
{
item.ParentCollection = this;
base.Add(item);
}
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
item.ParentCollection = this;
// Initialize values
}
// Generic method - I would like to call this directly from command handler regardless of type T
public void MoveItemUp(object parameter)
{
}
}
用法:
CustomObservableCollection<Model1> Model1Collection = new (listOfModel1instances);
我也一直在尝试使用接口,但没有成功。
编辑1
我需要添加一些我在开始时忘记提及的信息。
在我的 CustomObservableCollection 类中,我希望有一些通用方法处理属于参数给定的 Item 的集合,例如
public int GetMaxId(t item)
{
return item.Source.Max(i => i.Id);
}
我得到的: 错误 CS1061 'ICollection' 不包含 'Max' 的定义 ...
(给出的例子没有多大意义。但让我们假设,所有项目都具有 Order 属性。在命令处理程序中,我想对集合中的项目重新排序,例如,向上/向下移动选定的项目。在命令处理程序中,我所拥有的只是选定的项目由命令参数给出。
这将允许我对所有类型的项目只有一个命令。)
您的问题(以及您提供的附加信息)表明您集合中的项目需要访问父集合并且必须提供
Id
属性,例如,以便父集合可以使用 linq Max
方法。
解决这两个目标的一种方法是定义一个像
ISourceAware
这样的接口,并通过在其声明中添加CustomObservableCollection<T>
来约束where T: ISourceAware
只接受实现它的项目。
public interface ISourceAware
{
ICollection Source { get; set; }
int Id { get; }
}
public class CustomObservableCollection<T> : ObservableCollection<T> where T: ISourceAware
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
if(e.Action.Equals(NotifyCollectionChangedAction.Add))
{
foreach (ISourceAware iitem in e.NewItems)
{
iitem.Source = this;
#if DEBUG
var item = (Item)iitem;
// Try a loopback test
Console.WriteLine(
$"Index of {item.Description} is {item.ParentCollection.IndexOf(item)}");
#endif
}
}
}
}
集合处理自己的
CollectionChanged
通知,将自己设置为添加到其中的任何项目的 Source
。这解决了存储其父 ObservableCollection 的主要问题 - 您可以使用它进行验证,但您认为合适。
实现
ISourceAware
的类的最小示例:
public class Item : ISourceAware
{
public string Description { get; set; } = string.Empty;
public ICollection Source { get; set; }
public CustomObservableCollection<Item> ParentCollection =>
(CustomObservableCollection<Item>)Source;
public int Id { get; set; }
}
测试
这是我为测试这个答案而编写的快速控制台代码。
static void Main(string[] args)
{
Console.Title = "Test ISourceAware";
var collection = new CustomObservableCollection<Item>();
collection.Add(new Item { Description = "Item 1", Id = 1 });
collection.Add(new Item { Description = "Item 2", Id = 2 });
Console.WriteLine($"Min: {collection.Min(_ => _.Id)}");
Console.WriteLine($"Max: {collection.Max(_ => _.Id)}");
Console.ReadKey();
}
回复问题编辑
你发帖:
在我的 CustomObservableCollection 类中,我希望有一些通用方法处理属于参数给定的 Item 的集合,例如
public int GetMaxId(t item)
{
return item.Source.Max(i => i.Id);
}
通常当你有一个
interface
类型时,你会想将它转换为它的基础类型,这是一个正确的方法的例子。
public class CustomObservableCollection<T> : ObservableCollection<T> where T: ISourceAware
{
.
.
.
public static int GetMaxId(T item)
{
if (item.Source is ObservableCollection<T> items)
{
return items.Max(i => i.Id);
}
else throw new ArgumentException("Source must be an ObservableCollection<T>");
}
}