Загвоздка в том, что Я не хочу, чтобы эта пользовательская коллекция имела свойство 'Item' или 'Children', чтобы кто-нибудь мог случайно вызвать Add(item) или Remove(item) -подобные методы для этих обычно открытых свойств. Что я хочу сделать в XAML:
Код: Выделить всё
Детали реализации на данный момент (предоставлены для того, чтобы дать контекст того, с какой реализацией мы имеем дело):
Ниже приведено краткое описание модели классов, некоторые из которых представляют собой коллекции сами по себе, созданные таким образом с целью прямой ссылки сами в XAML, а не один из членов их общедоступной собственности в качестве узла подэлементов. Все происходит от общих абстрактных классов со множеством защищенных виртуальных методов (это требование):
Код: Выделить всё
// Hierarchy
// System.Object
// ∟ class NotifyPropertyChangedBase : INotifyPropertyChanged
// ∟ class ItemModel
// ∟ class VisualItemModel
// ∟ class VisualCollectionModel
// ∟ class VisualCollectionModel
// ∟ class HierarchicalCollectionModel
// ∟ class SomeFinalUsableClass
public abstract class ItemModel: NotifyPropertyChangedBase {
public bool IsSelected { get; set; }
public bool IsVisible { get; set; }
public virtual void ItemAddedToCollection(ItemModel collection) { ... }
}
public abstract class VisualItemModel: ItemModel {
public bool IsExpanded { get; set; }
}
public abstract class VisualCollectionModel :
VisualItemModel,
IList, ICollection, IList, ICollection,
IEnumerable, IEnumerable
where TItem : VisualItemModel {
// This is the internal collection
protecte ObservableCollection ItemsCollection {
get { ... }
}
object IList.this[int index] {
get {...}
set {...}
}
public TItem this[int index] {
get {...}
set {...}
}
protected virtual Int32 AddItemDirect(TItem item) {
ItemsCollection.Add(item);
return ItemsCollection.Count - 1;
}
public Int32 Add(TItem item) {
return AddItemDirect(item); // Call overridable method...
}
public Int32 Add(object obj) {
if (obj == null) return -1;
var item = obj as TItem;
return AddItemDirect(item); // Call overridable method...
}
bool IList.Contains(object obj) { ... }
bool ICollection.Contains(TItem item) { ... }
// ^^ all interface members are implemented
}
public abstract class HierarchicalCollectionModel
where TClass : VisualItemModel
where where TParent : VisualCollectionModel
where where TItem : VisualItemModel {
TParent Parent { get...; } // To have access to ancestors
protected override void ItemAdded(TItem item) { ... }
public override void ItemAddedToCollection(ItemModel collection) {
var parent = collection as TParent;
if (parent != null) SetParent(parent, true);
base.ItemAddedToCollection(collection);
}
}
// then a final class would derive from HierarchicalCollectionModel
Код: Выделить всё
// Implementation for a single item type in TreeView
public class FileSystemItem :
HierarchicalCollectionModel,
IComparable {
protected override Int32 AddItemDirect(TItem item) {
ItemsCollection.InsertSorted(item);
// InsertSorted is a custom generic extension method
// to INSERT the IComparable TItem at the correct position in collection;
// that's why ObservableCollection.Add() is to never be accessed directly
}
protected override void BeforeParentChanged(
FileSystemItem oldParent, FileSystemItem newParent) {
// inform child items their ancestor has vanished...
// inform ancestors they have lost one descendant...
}
protected override void ProcessIsExpandedStateChanged(bool newState) {
if (newState) SetIcon16(SelectedDirectoryIcon16, true);
else SetIcon16(DirectoryIcon16, true);
base.ProcessIsExpandedStateChanged(newState);
}
}
Код: Выделить всё
// Derived classes for any amount of different types in TreeView,
// yet, strongly constrained alltogether
public class RootNodeItem :
HierarchicalCollectionModel,
IComparable {
}
public class YearNodeItem :
HierarchicalCollectionModel,
IComparable {
public Int32 Year { get; set; }
}
public class MonthNodeItem :
HierarchicalCollectionModel,
IComparable {
public byte Month { get; set; }
}
public class DayNodeItem :
HierarchicalCollectionModel,
IComparable {
public byte Day { get; set; }
}
public class EntryItem: VisualItem, IComparable {
// ...
}
Когда я раскрываю ObservableCollection в производных классах, все работает отлично, вот так:
Код: Выделить всё
public class FileSystemItem :
HierarchicalCollectionModel,
IComparable {
public ObservabeCollection Items {
// The property member I reference in binding,
// with appropriate PropertyChanged implementation
// that fires just perfect for every cases (add/remove/insert/clear...)
return ItemsCollection; // this is the underlying collection.
}
}
Код: Выделить всё
SomeFileSystemItem.Items.Add(someItemOutOfTheBlue);Это обходит все необходимые проверки, правильное положение вставки в коллекцию (
Код: Выделить всё
IComparableЯ знаю, что могу отсортировать коллекцию после добавления элементов и выполнить соответствующие проверки после добавления любого лямбда-элемента, НО я имею дело с несколькими типами данных, которые поступают по сотням элементов на узел, большинство из них обновляются в вопрос секунд: это действующий пул ожидающих элементов, обработанных элементов, входящих элементов, наиболее отсортированных по дате для всей задачи обновления архива на протяжении двух десятилетий данных, в то время как новые обновления появляются в реальном времени, источниками которых являются различные типы файлов/архивов, записанные аудиовызовы, десять тысяч на локальном жестком диске и, возможно, миллион через локальную сеть, в то время как сеть постоянно подает потоки новых данных, которые могут быть связаны со старыми данными, когда они обновляются.
Я тот, кто закладывает основу специального приложения чтобы легко группировать и отображать эти архивы для прямого манипулирования, не опасаясь потери данных и иерархии. Я не тот, кто на самом деле определяет, как эти данные должны быть нормализованы/обновлены/сериализованы; таким образом, конечный пользователь будет иметь доступ к большинству классов и к этому заманчивому свойству «Item» или «Children», которое сделает его/ее работу настолько простой; вместо этого это, скорее всего, повредит все данные (например, недействительные/сломанные '
Код: Выделить всё
ParentВот почему важно, чтобы я никогда не предоставлял прямой доступ к базовой коллекции, в противном случае альтернативой является перехват непосредственно добавленного элемента, обнаружение того, что он не прошел надлежащие проверки, такие как принадлежность к соответствующему TreeView, удаление его из коллекции, а затем добавление его снова правильным способом, возможно, в виде клона. В процессе это приведет к увеличению количества PropertyChanged...
^^ Да, это крайний случай 0,001%, с которым никто не хочет иметь дело; Я тоже! но у меня заканчиваются варианты, так как я не понимаю, почему в TreeView отображается корневой узел моей коллекции, а не его элементы. TreeView ItemsSource — самый верхний элемент, он отображается. Но HierarchicalDataTemplate ItemsSource, похоже, не действует как коллекция с перечисляемыми элементами при ссылке на себя, вместо этого мне приходится предоставлять базовую коллекцию как свойство и указывать Path=Items в привязке.
Подробнее: https://stackoverflow.com/questions/799 ... a-property