diff --git a/src/AtomUI.Controls.DataGrid/Column/DataGridColumn.cs b/src/AtomUI.Controls.DataGrid/Column/DataGridColumn.cs index 90913c673..fc0432b42 100644 --- a/src/AtomUI.Controls.DataGrid/Column/DataGridColumn.cs +++ b/src/AtomUI.Controls.DataGrid/Column/DataGridColumn.cs @@ -248,9 +248,17 @@ public abstract partial class DataGridColumn : AvaloniaObject public bool CanUserFilter { - get => CanUserResizeInternal ?? - OwningGrid?.CanUserFilterColumns ?? - DataGrid.DefaultCanUserResizeColumns; + get + { + var canUserFilter = CanUserResizeInternal ?? + OwningGrid?.CanUserFilterColumns ?? + DataGrid.DefaultCanUserFilterColumns; + if (HasHeaderCell) + { + HeaderCell.CanUserFilter = canUserFilter; + } + return canUserFilter; + } set { CanUserFilterInternal = value; diff --git a/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeader.Filter.cs b/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeader.Filter.cs index 9f42aee6b..6287d2333 100644 --- a/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeader.Filter.cs +++ b/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeader.Filter.cs @@ -4,6 +4,7 @@ // All other rights reserved. using System.Diagnostics; +using AtomUI.Controls.Data; using Avalonia; namespace AtomUI.Controls; @@ -15,29 +16,111 @@ internal partial class DataGridColumnHeader nameof(CanUserFilter), o => o.CanUserFilter, (o, v) => o.CanUserFilter = v); - + private bool _canUserFilter = false; + internal bool CanUserFilter { get => _canUserFilter; set => SetAndRaise(CanUserFilterProperty, ref _canUserFilter, value); } - - internal void InvokeProcessSort() + + private DataGridFilterIndicator? _filterIndicator; + + internal void InvokeProcessFilter(List filterValues) { Debug.Assert(OwningGrid != null); - if (OwningGrid.WaitForLostFocus(InvokeProcessSort)) + if (OwningGrid.WaitForLostFocus(() => InvokeProcessFilter(filterValues))) { return; } + if (OwningGrid.CommitEdit(DataGridEditingUnit.Row, exitEditingMode: true)) { - Avalonia.Threading.Dispatcher.UIThread.Post(ProcessSort); + Avalonia.Threading.Dispatcher.UIThread.Post(() => ProcessFilter(filterValues)); } } - internal void ProcessSort() + internal void ProcessFilter(List filterValues) { - + if (OwningColumn != null && + OwningGrid != null && + OwningGrid.EditingRow == null && + OwningColumn != OwningGrid.ColumnsInternal.FillerColumn && + (OwningColumn.CanUserFilter || OwningGrid.CanUserFilterColumns)) + { + var ea = new DataGridColumnEventArgs(OwningColumn); + OwningGrid.HandleColumnFiltering(ea); + if (!ea.Handled && OwningGrid.DataConnection.AllowFilter && + OwningGrid.DataConnection.FilterDescriptions != null) + { + DataGrid owningGrid = OwningGrid; + DataGridFilterDescription? newFilter; + DataGridFilterDescription? filter = OwningColumn.GetFilterDescription(); + IDataGridCollectionView? collectionView = owningGrid.DataConnection.CollectionView; + Debug.Assert(collectionView != null); + using (collectionView.DeferRefresh()) + { + if (owningGrid.DataConnection.FilterDescriptions.Count == 0) + { + owningGrid.DataConnection.FilterDescriptions.Clear(); + } + + if (filter != null) + { + // 比较一下值,如果过滤的值不相等就重新添加 + var oldFilterValues = filter.FilterConditions.ToHashSet(); + var newFilterValues = filterValues.ToHashSet(); + if (!oldFilterValues.SetEquals(newFilterValues)) + { + newFilter = new DataGridFilterDescription() + { + PropertyPath = filter.PropertyPath, + Filter = filter.Filter, + FilterConditions = filterValues.Cast().ToList(), + }; + int oldIndex = owningGrid.DataConnection.FilterDescriptions.IndexOf(filter); + if (oldIndex >= 0) + { + owningGrid.DataConnection.FilterDescriptions.Remove(filter); + owningGrid.DataConnection.FilterDescriptions.Insert(oldIndex, newFilter); + } + else + { + owningGrid.DataConnection.FilterDescriptions.Add(newFilter); + } + } + } + else + { + string? propertyName = OwningColumn.GetFilterPropertyName(); + if (string.IsNullOrEmpty(propertyName)) + { + return; + } + newFilter = new DataGridFilterDescription() + { + PropertyPath = propertyName, + Filter = OwningColumn.OnFilter, + FilterConditions = filterValues.Cast().ToList(), + }; + owningGrid.DataConnection.FilterDescriptions.Add(newFilter); + } + } + } + } + } + + private void ConfigureFilterIndicator() + { + if (_filterIndicator != null) + { + _filterIndicator.FilterRequest += HandleFilterRequest; + } + } + + private void HandleFilterRequest(object? sender, DataGridColumnFilterEventArgs args) + { + InvokeProcessFilter(args.FilterValues); } } \ No newline at end of file diff --git a/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeader.cs b/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeader.cs index 38cfe5fb4..a4f9fa765 100644 --- a/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeader.cs +++ b/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeader.cs @@ -201,7 +201,6 @@ internal partial class DataGridColumnHeader : ContentControl private bool _desiredSeparatorVisibility = true; private StackPanel? _indicatorsLayout; private static Lazy _resizeCursor = new Lazy(() => new Cursor(StandardCursorType.SizeWestEast)); - private DataGridFilterIndicator? _filterIndicator; private Border? _frame; static DataGridColumnHeader() @@ -863,6 +862,8 @@ internal partial class DataGridColumnHeader : ContentControl { _filterIndicator.OwningColumn = OwningColumn; } + + ConfigureFilterIndicator(); if (_indicatorsLayout != null) { _indicatorsLayout.Children.CollectionChanged += HandleIndicatorLayoutChildrenChanged; diff --git a/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridFilterIndicator.cs b/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridFilterIndicator.cs index 971c157ab..88ae3f031 100644 --- a/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridFilterIndicator.cs +++ b/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridFilterIndicator.cs @@ -26,6 +26,8 @@ internal class DataGridFilterIndicator : IconButton public static readonly StyledProperty FilterMultipleProperty = AvaloniaProperty.Register(nameof(FilterMultiple)); + + public event EventHandler? FilterRequest; public bool IsFilterActivated { @@ -242,6 +244,11 @@ internal class DataGridFilterIndicator : IconButton private void HandleFilterValuesSelected(object? sender, DataGridFilterValuesSelectedEventArgs args) { + Debug.Assert(OwningColumn != null); + if (OwningColumn.FilterOnClose || args.IsConfirmed) + { + FilterRequest?.Invoke(this, new DataGridColumnFilterEventArgs(OwningColumn, args.Values)); + } } protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) diff --git a/src/AtomUI.Controls.DataGrid/Data/DataGridDataConnection.cs b/src/AtomUI.Controls.DataGrid/Data/DataGridDataConnection.cs index aa42ad725..9f360b5db 100644 --- a/src/AtomUI.Controls.DataGrid/Data/DataGridDataConnection.cs +++ b/src/AtomUI.Controls.DataGrid/Data/DataGridDataConnection.cs @@ -101,6 +101,22 @@ internal class DataGridDataConnection return CollectionView.CanSort; } } + + /// + /// True if the collection view says it can filter. + /// + public bool AllowFilter + { + get + { + if (CollectionView == null || + (EditableCollectionView != null && (EditableCollectionView.IsAddingNew || EditableCollectionView.IsEditingItem))) + { + return false; + } + return CollectionView.CanFilter; + } + } public bool CommittingEdit { get; private set; } diff --git a/src/AtomUI.Controls.DataGrid/Data/DataGridFilterDescription.cs b/src/AtomUI.Controls.DataGrid/Data/DataGridFilterDescription.cs index 75e209e78..635946858 100644 --- a/src/AtomUI.Controls.DataGrid/Data/DataGridFilterDescription.cs +++ b/src/AtomUI.Controls.DataGrid/Data/DataGridFilterDescription.cs @@ -8,8 +8,7 @@ public class DataGridFilterDescription public string? PropertyPath { get; set; } public bool HasPropertyPath => !string.IsNullOrEmpty(PropertyPath); public StringComparison ComparisonType { get; } = StringComparison.InvariantCultureIgnoreCase; - public bool IsEnabled { get; set; } - public int ColumnIndex { get; set; } + public List FilterConditions { get; set; } = new List(); public Func? Filter { get; set; } diff --git a/src/AtomUI.Controls.DataGrid/DataGrid.Columns.cs b/src/AtomUI.Controls.DataGrid/DataGrid.Columns.cs index 5133655ad..bab262b1f 100644 --- a/src/AtomUI.Controls.DataGrid/DataGrid.Columns.cs +++ b/src/AtomUI.Controls.DataGrid/DataGrid.Columns.cs @@ -119,6 +119,11 @@ public partial class DataGrid { Sorting?.Invoke(this, e); } + + protected internal virtual void HandleColumnFiltering(DataGridColumnEventArgs e) + { + Filtering?.Invoke(this, e); + } /// /// Adjusts the widths of all columns with DisplayIndex >= displayIndex such that the total diff --git a/src/AtomUI.Controls.DataGrid/DataGrid.cs b/src/AtomUI.Controls.DataGrid/DataGrid.cs index e57ad9175..f1da525df 100644 --- a/src/AtomUI.Controls.DataGrid/DataGrid.cs +++ b/src/AtomUI.Controls.DataGrid/DataGrid.cs @@ -718,6 +718,11 @@ public partial class DataGrid : TemplatedControl, /// Occurs when the sorting request is triggered. /// public event EventHandler? Sorting; + + /// + /// Occurs when the filtering request is triggered. + /// + public event EventHandler? Filtering; /// /// Occurs when a diff --git a/src/AtomUI.Controls.DataGrid/EventArgs/DataGridColumnFilterEventArgs.cs b/src/AtomUI.Controls.DataGrid/EventArgs/DataGridColumnFilterEventArgs.cs index fff736883..823f749be 100644 --- a/src/AtomUI.Controls.DataGrid/EventArgs/DataGridColumnFilterEventArgs.cs +++ b/src/AtomUI.Controls.DataGrid/EventArgs/DataGridColumnFilterEventArgs.cs @@ -2,16 +2,13 @@ namespace AtomUI.Controls; public class DataGridColumnFilterEventArgs : EventArgs { - public DataGridFilterMode FilterMode { get; } public List FilterValues { get; } public DataGridColumn Column { get; } - public DataGridColumnFilterEventArgs(DataGridColumn column, DataGridFilterMode filterMode, - List filterValues) + public DataGridColumnFilterEventArgs(DataGridColumn column, List filterValues) { Column = column; - FilterMode = filterMode; FilterValues = filterValues; } } \ No newline at end of file