using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; namespace FasdDesktopUi.Basics.Models { public class HierarchicalSelectionItem : INotifyPropertyChanged { private bool _isExpanded; public string Id { get; set; } public string DisplayName { get; set; } public string ParentId { get; set; } public string ParentDisplayName { get; set; } public ObservableCollection Children { get; } = new ObservableCollection(); public HierarchicalSelectionItem Parent { get; private set; } public bool IsExpanded { get => _isExpanded; set { if (_isExpanded == value) return; _isExpanded = value; OnPropertyChanged(nameof(IsExpanded)); } } public string FullPath { get { if (Parent == null || string.IsNullOrWhiteSpace(Parent.DisplayName)) return DisplayName ?? string.Empty; if (string.IsNullOrWhiteSpace(DisplayName)) return Parent.FullPath; return $"{Parent.FullPath} / {DisplayName}"; } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); public void AddChild(HierarchicalSelectionItem child) { if (child == null) return; child.Parent = this; Children.Add(child); } public void ClearChildren() { foreach (var child in Children) child.Parent = null; Children.Clear(); } public void SortChildrenRecursive() { if (Children == null || Children.Count == 0) return; var orderedChildren = Children .OrderBy(child => child.DisplayName, StringComparer.CurrentCultureIgnoreCase) .ToList(); Children.Clear(); foreach (var child in orderedChildren) { Children.Add(child); child.SortChildrenRecursive(); } } public IEnumerable SelfAndDescendants() { yield return this; foreach (var child in Children) { foreach (var descendant in child.SelfAndDescendants()) yield return descendant; } } public void SetExpandedRecursive(bool isExpanded) { IsExpanded = isExpanded; foreach (var child in Children) child.SetExpandedRecursive(isExpanded); } public HierarchicalSelectionItem CloneWithoutChildren() { return new HierarchicalSelectionItem { Id = Id, DisplayName = DisplayName, ParentId = ParentId, ParentDisplayName = ParentDisplayName }; } public HierarchicalSelectionItem CloneBranch(Func predicate) { bool matches = predicate?.Invoke(this) ?? true; var matchingChildren = new List(); foreach (var child in Children) { var childClone = child.CloneBranch(predicate); if (childClone != null) matchingChildren.Add(childClone); } if (!matches && matchingChildren.Count == 0) return null; var clone = CloneWithoutChildren(); foreach (var childClone in matchingChildren) clone.AddChild(childClone); return clone; } public static ObservableCollection BuildTree(IEnumerable items) { if (items == null) return new ObservableCollection(); var lookup = items .Where(item => !string.IsNullOrWhiteSpace(item?.Id)) .GroupBy(item => item.Id) .Select(group => group.First()) .ToDictionary(item => item.Id); foreach (var entry in lookup.Values) entry.ClearChildren(); var roots = new List(); foreach (var item in lookup.Values) { if (!string.IsNullOrWhiteSpace(item.ParentId) && lookup.TryGetValue(item.ParentId, out var parent)) { parent.AddChild(item); } else { item.Parent = null; roots.Add(item); } } foreach (var root in roots) root.SortChildrenRecursive(); return new ObservableCollection( roots.OrderBy(root => root.DisplayName, StringComparer.CurrentCultureIgnoreCase)); } } }