| | | 1 | | // ----------------------------------------------------------------------- |
| | | 2 | | // <copyright file="GroupingViewModel.cs" company="Stéphane ANDRE"> |
| | | 3 | | // Copyright (c) Stéphane ANDRE. All rights reserved. |
| | | 4 | | // </copyright> |
| | | 5 | | // ----------------------------------------------------------------------- |
| | | 6 | | |
| | | 7 | | using System; |
| | | 8 | | using System.Collections.Generic; |
| | | 9 | | using System.Collections.ObjectModel; |
| | | 10 | | using System.ComponentModel; |
| | | 11 | | using System.Linq; |
| | | 12 | | using MyNet.Collections; |
| | | 13 | | using MyNet.Observable; |
| | | 14 | | using MyNet.Observable.Collections.Grouping; |
| | | 15 | | using MyNet.Utilities.Deferring; |
| | | 16 | | |
| | | 17 | | namespace MyNet.UI.ViewModels.List.Grouping; |
| | | 18 | | |
| | | 19 | | /// <summary> |
| | | 20 | | /// Represents a view model for managing grouping configuration of a collection. |
| | | 21 | | /// </summary> |
| | | 22 | | /// <typeparam name="T">The type of items in the collection.</typeparam> |
| | | 23 | | public class GroupingViewModel<T> : ObservableObject, IGroupingViewModel<T> |
| | | 24 | | { |
| | | 25 | | private readonly DeferredAction _deferredAction; |
| | | 26 | | |
| | | 27 | | /// <summary> |
| | | 28 | | /// Creates a fluent builder used to configure and instantiate a <see cref="GroupingViewModel{T}"/>. |
| | | 29 | | /// </summary> |
| | 0 | 30 | | public static GroupingViewModelBuilder<T> CreateBuilder() => new(); |
| | | 31 | | |
| | | 32 | | /// <summary> |
| | | 33 | | /// Initializes a new instance of the <see cref="GroupingViewModel{T}"/> class with the specified grouping propertie |
| | | 34 | | /// </summary> |
| | | 35 | | /// <param name="properties">The grouping properties for the collection.</param> |
| | | 36 | | /// <param name="defaultGrouping">The optional default grouping configuration.</param> |
| | 0 | 37 | | public GroupingViewModel(IEnumerable<IGroupingPropertyViewModel<T>> properties, IEnumerable<IGroupingProperty<T>>? d |
| | | 38 | | { |
| | 0 | 39 | | Properties = new(new(properties)); |
| | 0 | 40 | | DefaultGrouping = defaultGrouping?.ToList() ?? []; |
| | | 41 | | |
| | 0 | 42 | | _deferredAction = new(RaiseGroupingChanged); |
| | | 43 | | |
| | 0 | 44 | | Reset(); |
| | | 45 | | |
| | 0 | 46 | | foreach (var property in Properties) |
| | 0 | 47 | | property.PropertyChanged += HandlePropertyChanged; |
| | 0 | 48 | | } |
| | | 49 | | |
| | | 50 | | /// <summary> |
| | | 51 | | /// Gets the collection of grouping property view models that can be configured for grouping. This collection is rea |
| | | 52 | | /// </summary> |
| | | 53 | | public ReadOnlyObservableCollection<IGroupingPropertyViewModel<T>> Properties { get; } |
| | | 54 | | |
| | | 55 | | /// <summary> |
| | | 56 | | /// Gets the current grouping configuration built from the UI. This collection is read-only and represents the activ |
| | | 57 | | /// </summary> |
| | 0 | 58 | | public IReadOnlyList<IGroupingProperty<T>> CurrentGrouping { get; private set => SetProperty(ref field, value); } = |
| | | 59 | | |
| | | 60 | | /// <summary> |
| | | 61 | | /// Gets the default grouping configuration for the collection. This collection is read-only and represents the init |
| | | 62 | | /// </summary> |
| | | 63 | | public IReadOnlyList<IGroupingProperty<T>> DefaultGrouping { get; } |
| | | 64 | | |
| | | 65 | | /// <summary> |
| | | 66 | | /// Gets a value indicating whether there are any active grouping properties in the current configuration. This prop |
| | | 67 | | /// </summary> |
| | 0 | 68 | | public bool HasActiveGrouping => CurrentGrouping.Any(); |
| | | 69 | | |
| | | 70 | | /// <summary> |
| | | 71 | | /// Occurs when the grouping configuration has changed. Subscribers can react to this event to apply the new groupin |
| | | 72 | | /// </summary> |
| | | 73 | | public event EventHandler<GroupingChangedEventArgs<T>>? GroupingChanged; |
| | | 74 | | |
| | | 75 | | /// <summary> |
| | | 76 | | /// Applies the current grouping configuration. This method triggers the grouping update process by invoking the def |
| | | 77 | | /// </summary> |
| | 0 | 78 | | public void Apply() => _deferredAction.Request(); |
| | | 79 | | |
| | | 80 | | /// <summary> |
| | | 81 | | /// Clears all active grouping by disabling all grouping properties. This method iterates through the Properties col |
| | | 82 | | /// </summary> |
| | | 83 | | public void Clear() |
| | | 84 | | { |
| | 0 | 85 | | using (_deferredAction.Defer()) |
| | 0 | 86 | | Properties.ForEach(p => p.IsEnabled = false); |
| | 0 | 87 | | } |
| | | 88 | | |
| | | 89 | | /// <summary> |
| | | 90 | | /// Resets the grouping configuration to its default state. This method first clears all active grouping properties |
| | | 91 | | /// </summary> |
| | | 92 | | public void Reset() |
| | | 93 | | { |
| | 0 | 94 | | using (_deferredAction.Defer()) |
| | | 95 | | { |
| | 0 | 96 | | Clear(); |
| | | 97 | | |
| | 0 | 98 | | foreach (var def in DefaultGrouping) |
| | | 99 | | { |
| | 0 | 100 | | var vm = FindMatching(def); |
| | | 101 | | |
| | 0 | 102 | | vm?.IsEnabled = true; |
| | | 103 | | } |
| | | 104 | | } |
| | 0 | 105 | | } |
| | | 106 | | |
| | | 107 | | /// <summary> |
| | | 108 | | /// Sets the active state of a grouping property identified by the specified key. This method finds the grouping pro |
| | | 109 | | /// </summary> |
| | | 110 | | /// <param name="key">The key of the grouping property to update.</param> |
| | | 111 | | /// <param name="isActive">A value indicating whether the grouping property should be active.</param> |
| | | 112 | | public void SetActive(string key, bool isActive) |
| | | 113 | | { |
| | 0 | 114 | | var property = Find(key); |
| | | 115 | | |
| | 0 | 116 | | if (property is null) return; |
| | | 117 | | |
| | 0 | 118 | | using (_deferredAction.Defer()) |
| | 0 | 119 | | property.IsEnabled = isActive; |
| | 0 | 120 | | } |
| | | 121 | | |
| | | 122 | | /// <summary> |
| | | 123 | | /// Computes the current grouping configuration based on the enabled grouping properties in the Properties collectio |
| | | 124 | | /// </summary> |
| | | 125 | | /// <returns>A read-only list of the current grouping properties.</returns> |
| | | 126 | | private IReadOnlyList<IGroupingProperty<T>> ComputeCurrentGrouping() => |
| | 0 | 127 | | [ |
| | 0 | 128 | | .. Properties |
| | 0 | 129 | | .Where(p => p.IsEnabled) |
| | 0 | 130 | | .OrderBy(p => p.ActivatedAt) |
| | 0 | 131 | | .Select(p => p.Build()) |
| | 0 | 132 | | ]; |
| | | 133 | | |
| | | 134 | | /// <summary> |
| | | 135 | | /// Handles the PropertyChanged event for the grouping property view models. When any property of a grouping propert |
| | | 136 | | /// </summary> |
| | | 137 | | /// <param name="sender">The source of the event.</param> |
| | | 138 | | /// <param name="e">The event data.</param> |
| | 0 | 139 | | private void HandlePropertyChanged(object? sender, PropertyChangedEventArgs e) => _deferredAction.Request(); |
| | | 140 | | |
| | | 141 | | /// <summary> |
| | | 142 | | /// Handles the logic for when the grouping configuration has changed. This method computes the current grouping con |
| | | 143 | | /// </summary> |
| | | 144 | | private void RaiseGroupingChanged() |
| | | 145 | | { |
| | 0 | 146 | | CurrentGrouping = ComputeCurrentGrouping(); |
| | 0 | 147 | | GroupingChanged?.Invoke(this, new(CurrentGrouping)); |
| | 0 | 148 | | } |
| | | 149 | | |
| | | 150 | | /// <summary> |
| | | 151 | | /// Finds a grouping property view model in the Properties collection by its unique key. This method searches throug |
| | | 152 | | /// </summary> |
| | | 153 | | /// <param name="key">The unique key of the grouping property to find.</param> |
| | | 154 | | /// <returns>The grouping property view model with the specified key, or null if not found.</returns> |
| | | 155 | | private IGroupingPropertyViewModel<T>? Find(string key) |
| | 0 | 156 | | => Properties.FirstOrDefault(p => p.Key == key); |
| | | 157 | | |
| | | 158 | | /// <summary> |
| | | 159 | | /// Finds a grouping property view model in the Properties collection that matches the specified grouping property. |
| | | 160 | | /// </summary> |
| | | 161 | | /// <param name="property">The grouping property to match.</param> |
| | | 162 | | /// <returns>The grouping property view model that matches the specified grouping property, or null if not found.</r |
| | | 163 | | private IGroupingPropertyViewModel<T>? FindMatching(IGroupingProperty<T> property) |
| | 0 | 164 | | => Properties.FirstOrDefault(p => p.Matches(property)); |
| | | 165 | | |
| | | 166 | | /// <inheritdoc /> |
| | | 167 | | protected override void DisposeManagedResources() |
| | | 168 | | { |
| | 0 | 169 | | foreach (var property in Properties) |
| | 0 | 170 | | property.PropertyChanged -= HandlePropertyChanged; |
| | | 171 | | |
| | 0 | 172 | | base.DisposeManagedResources(); |
| | 0 | 173 | | } |
| | | 174 | | } |
| | | 175 | | |