diff --git a/src/AtomUI.Base/MotionScene/AbstractMotion.cs b/src/AtomUI.Base/MotionScene/AbstractMotion.cs
index fbcdcfd8b..2e42ea474 100644
--- a/src/AtomUI.Base/MotionScene/AbstractMotion.cs
+++ b/src/AtomUI.Base/MotionScene/AbstractMotion.cs
@@ -114,16 +114,14 @@ internal class AbstractMotion : IMotion
}
else
{
- observables.Zip()
- .LastAsync()
- .ObserveOn(AvaloniaScheduler.Instance)
- .Subscribe(_ =>
- {
- Dispatcher.UIThread.Post(() =>
- {
- FinishedCallback();
- });
- });
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ {
+ await observables.Zip()
+ .LastAsync()
+ .ObserveOn(AvaloniaScheduler.Instance)
+ .ToTask();
+ FinishedCallback();
+ });
}
});
}
diff --git a/src/AtomUI.Controls.DataGrid/AtomUIDataGridThemesProvider.axaml b/src/AtomUI.Controls.DataGrid/AtomUIDataGridThemesProvider.axaml
index d848f2381..7ddd57b41 100644
--- a/src/AtomUI.Controls.DataGrid/AtomUIDataGridThemesProvider.axaml
+++ b/src/AtomUI.Controls.DataGrid/AtomUIDataGridThemesProvider.axaml
@@ -16,5 +16,7 @@
+
+
\ No newline at end of file
diff --git a/src/AtomUI.Controls.DataGrid/Column/DataGridColumn.cs b/src/AtomUI.Controls.DataGrid/Column/DataGridColumn.cs
index f357ae649..facd43566 100644
--- a/src/AtomUI.Controls.DataGrid/Column/DataGridColumn.cs
+++ b/src/AtomUI.Controls.DataGrid/Column/DataGridColumn.cs
@@ -249,7 +249,7 @@ public abstract partial class DataGridColumn : AvaloniaObject
public bool CanUserFilter
{
get => CanUserResizeInternal ??
- OwningGrid?.CanUserResizeColumns ??
+ OwningGrid?.CanUserFilterColumns ??
DataGrid.DefaultCanUserResizeColumns;
set
{
@@ -424,6 +424,11 @@ public abstract partial class DataGridColumn : AvaloniaObject
///
public DataGridFilterMode FilterMode { get; set; }
+ ///
+ /// Whether multiple filters can be selected
+ ///
+ public bool FilterMultiple { get; set; } = true;
+
///
/// Whether to trigger filter when the filter menu closes
///
diff --git a/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeader.cs b/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeader.cs
index bf660f2b1..824d4366c 100644
--- a/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeader.cs
+++ b/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeader.cs
@@ -174,9 +174,21 @@ internal class DataGridColumnHeader : ContentControl
get => _canUserFilter;
set => SetAndRaise(CanUserFilterProperty, ref _canUserFilter, value);
}
-
- internal DataGridColumn? OwningColumn { get; set; }
-
+
+ private DataGridColumn? _owningColumn;
+ internal DataGridColumn? OwningColumn
+ {
+ get => _owningColumn;
+ set
+ {
+ _owningColumn = value;
+ if (_filterIndicator != null)
+ {
+ _filterIndicator.OwningColumn = OwningColumn;
+ }
+ }
+ }
+
internal DataGrid? OwningGrid => OwningColumn?.OwningGrid;
internal int ColumnIndex
@@ -212,6 +224,7 @@ internal class DataGridColumnHeader : ContentControl
private bool _desiredSeparatorVisibility = true;
private StackPanel? _indicatorsLayout;
private static Lazy _resizeCursor = new Lazy(() => new Cursor(StandardCursorType.SizeWestEast));
+ private DataGridFilterIndicator? _filterIndicator;
static DataGridColumnHeader()
{
@@ -971,6 +984,11 @@ internal class DataGridColumnHeader : ContentControl
{
base.OnApplyTemplate(e);
_indicatorsLayout = e.NameScope.Find(DataGridColumnHeaderThemeConstants.IndicatorsLayoutPart);
+ _filterIndicator = e.NameScope.Find(DataGridColumnHeaderThemeConstants.FilterIndicatorPart);
+ if (_filterIndicator != null && OwningColumn != null)
+ {
+ _filterIndicator.OwningColumn = OwningColumn;
+ }
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 4e1f28a10..b4dddcf80 100644
--- a/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridFilterIndicator.cs
+++ b/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridFilterIndicator.cs
@@ -1,5 +1,8 @@
+using System.Diagnostics;
+using AtomUI.Data;
using AtomUI.IconPkg.AntDesign;
using Avalonia;
+using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
@@ -12,13 +15,83 @@ internal class DataGridFilterIndicator : IconButton
public static readonly StyledProperty IsFilterActivatedProperty
= AvaloniaProperty.Register(nameof(IsFilterActivated));
+ public static readonly DirectProperty FilterModeProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(FilterMode),
+ o => o.FilterMode,
+ (o, v) => o.FilterMode = v);
+
+ public static readonly StyledProperty ToggleTypeProperty =
+ MenuItem.ToggleTypeProperty.AddOwner();
+
public bool IsFilterActivated
{
get => GetValue(IsFilterActivatedProperty);
set => SetValue(IsFilterActivatedProperty, value);
}
+ private DataGridFilterMode _filterMode;
+ public DataGridFilterMode FilterMode
+ {
+ get => _filterMode;
+ set => SetAndRaise(FilterModeProperty, ref _filterMode, value);
+ }
+
+ public MenuItemToggleType ToggleType
+ {
+ get => GetValue(ToggleTypeProperty);
+ set => SetValue(ToggleTypeProperty, value);
+ }
+
#endregion
+
+ private DataGridColumn? _owningColumn;
+ private static int _indicatorSeed = 0;
+ private string _radioCheckGroupName;
+
+ internal DataGridColumn? OwningColumn
+ {
+ get => _owningColumn;
+ set
+ {
+ _owningColumn = value;
+ if (_owningColumn != null)
+ {
+ FilterMode = _owningColumn.FilterMode;
+ ToggleType = _owningColumn.FilterMultiple ? MenuItemToggleType.CheckBox : MenuItemToggleType.Radio;
+ }
+ }
+ }
+ private readonly FlyoutStateHelper _flyoutStateHelper;
+
+ public DataGridFilterIndicator()
+ {
+ _flyoutStateHelper = new FlyoutStateHelper
+ {
+ AnchorTarget = this,
+ TriggerType = FlyoutTriggerType.Click,
+ ClickHideFlyoutPredicate = (provider, args) =>
+ {
+ if (provider.PopupHost != args.Root)
+ {
+ if (args.Root is Control control)
+ {
+ if (control.Parent is Visual visualParent)
+ {
+ var topLevel = TopLevel.GetTopLevel(visualParent);
+ if (provider.PopupHost == topLevel)
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ };
+ _radioCheckGroupName = $"{nameof(DataGridFilterIndicator)}-{_indicatorSeed++}";
+ }
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
@@ -26,6 +99,83 @@ internal class DataGridFilterIndicator : IconButton
{
SetValue(IconProperty, AntDesignIconPackage.FilterFilled(), BindingPriority.Template);
}
+
+ CreateFlyout();
base.OnApplyTemplate(e);
}
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs changed)
+ {
+ base.OnPropertyChanged(changed);
+ if (changed.Property == FilterModeProperty)
+ {
+ CreateFlyout();
+ }
+ }
+
+ private void CreateFlyout()
+ {
+ Debug.Assert(OwningColumn is not null);
+ Debug.Assert(OwningColumn.OwningGrid is not null);
+ var owningGrid = OwningColumn.OwningGrid;
+ if (FilterMode == DataGridFilterMode.Menu && Flyout is not DataGridMenuFilterFlyout)
+ {
+ var menuFlyout = new DataGridMenuFilterFlyout()
+ {
+ IsShowArrow = false,
+ Placement = PlacementMode.BottomEdgeAlignedRight,
+ IsDetectMouseClickEnabled = false
+ };
+ BindUtils.RelayBind(owningGrid, MotionAwareControlProperty.IsMotionEnabledProperty, menuFlyout, MotionAwareControlProperty.IsMotionEnabledProperty);
+ var menuItems = BuildMenuItems(OwningColumn.Filters.ToList());
+ foreach (var menuItem in menuItems)
+ {
+ menuFlyout.Items.Add(menuItem);
+ }
+ Flyout = menuFlyout;
+ _flyoutStateHelper.Flyout = menuFlyout;
+ }
+ else if (FilterMode == DataGridFilterMode.Tree && Flyout is not DataGridTreeFilterFlyout)
+ {
+ Flyout = new DataGridTreeFilterFlyout();
+ }
+ }
+
+ private List BuildMenuItems(List filterItems)
+ {
+ var menuItems = new List();
+ foreach (var item in filterItems)
+ {
+ var menuItem = new DataGridFilterMenuItem()
+ {
+ Header = item.Text,
+ FilterValue = item.Value,
+ StaysOpenOnClick = true,
+ GroupName = _radioCheckGroupName
+ };
+ BindUtils.RelayBind(this, ToggleTypeProperty, menuItem, ToggleTypeProperty);
+ menuItems.Add(menuItem);
+ if (item.Children.Count > 0)
+ {
+ var childItems = BuildMenuItems(item.Children);
+ foreach (var childItem in childItems)
+ {
+ menuItem.Items.Add(childItem);
+ }
+ }
+ }
+ return menuItems;
+ }
+
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+ _flyoutStateHelper.NotifyDetachedFromVisualTree();
+ }
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+ _flyoutStateHelper.NotifyAttachedToVisualTree();
+ }
}
\ No newline at end of file
diff --git a/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridMenuFilterFlyout.cs b/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridMenuFilterFlyout.cs
index 84de93109..f033090ce 100644
--- a/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridMenuFilterFlyout.cs
+++ b/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridMenuFilterFlyout.cs
@@ -1,6 +1,33 @@
+using AtomUI.Data;
+using Avalonia.Controls;
+
namespace AtomUI.Controls;
-public class DataGridMenuFilterFlyout
+internal class DataGridMenuFilterFlyout : MenuFlyout
{
-
+ protected override Control CreatePresenter()
+ {
+ var presenter = new DataGridMenuFilterFlyoutPresenter
+ {
+ ItemsSource = Items,
+ [!ItemsControl.ItemTemplateProperty] = this[!ItemTemplateProperty],
+ [!ItemsControl.ItemContainerThemeProperty] = this[!ItemContainerThemeProperty],
+ MenuFlyout = this
+ };
+ BindUtils.RelayBind(this, IsShowArrowEffectiveProperty, presenter, MenuFlyoutPresenter.IsShowArrowProperty);
+ BindUtils.RelayBind(this, IsMotionEnabledProperty, presenter, MenuFlyoutPresenter.IsMotionEnabledProperty);
+ SetupArrowPosition(Popup, presenter);
+ CalculateShowArrowEffective();
+ return presenter;
+ }
+}
+
+internal class DataGridFilterMenuItem : MenuItem
+{
+ public string? FilterValue { get; set; }
+
+ static DataGridFilterMenuItem()
+ {
+ StaysOpenOnClickProperty.OverrideDefaultValue(true);
+ }
}
\ No newline at end of file
diff --git a/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridMenuFilterFlyoutPresenter.cs b/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridMenuFilterFlyoutPresenter.cs
new file mode 100644
index 000000000..4efdae278
--- /dev/null
+++ b/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridMenuFilterFlyoutPresenter.cs
@@ -0,0 +1,5 @@
+namespace AtomUI.Controls;
+
+internal class DataGridMenuFilterFlyoutPresenter : MenuFlyoutPresenter
+{
+}
\ No newline at end of file
diff --git a/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridTreeFilterFlyout.cs b/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridTreeFilterFlyout.cs
index 24a65bd44..05e28b291 100644
--- a/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridTreeFilterFlyout.cs
+++ b/src/AtomUI.Controls.DataGrid/Column/Filters/DataGridTreeFilterFlyout.cs
@@ -1,6 +1,5 @@
namespace AtomUI.Controls;
-public class DataGridTreeFilterFlyout
+public class DataGridTreeFilterFlyout : Flyout
{
-
}
\ No newline at end of file
diff --git a/src/AtomUI.Controls.DataGrid/DataGrid.Columns.cs b/src/AtomUI.Controls.DataGrid/DataGrid.Columns.cs
index 665978db9..5133655ad 100644
--- a/src/AtomUI.Controls.DataGrid/DataGrid.Columns.cs
+++ b/src/AtomUI.Controls.DataGrid/DataGrid.Columns.cs
@@ -8,7 +8,6 @@ using System.Collections.Specialized;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Reflection;
-using AtomUI.Data;
using AtomUI.Utils;
using Avalonia.Controls;
using Avalonia.Data;
diff --git a/src/AtomUI.Controls.DataGrid/DataGridToken.cs b/src/AtomUI.Controls.DataGrid/DataGridToken.cs
index c096eedd5..a6797b739 100644
--- a/src/AtomUI.Controls.DataGrid/DataGridToken.cs
+++ b/src/AtomUI.Controls.DataGrid/DataGridToken.cs
@@ -217,7 +217,14 @@ internal class DataGridToken : AbstractControlDesignToken
public Color TableFixedHeaderSortActiveBg { get; set; }
public Color TableHeaderFilterActiveBg { get; set; }
public Color TableFilterDropdownBg { get; set; }
+ public double TableFilterButtonSpacing { get; set; }
+ public Thickness TableFilterButtonContainerMargin { get; set; }
+ public Thickness TableFilterButtonLayoutSeparatorMargin { get; set; }
+
public double TableFilterDropdownHeight { get; set; }
+ public double TableFilterDropdownWidth { get; set; }
+ public double TableFilterDropdownSearchWidth { get; set; }
+
public Color TableRowHoverBg { get; set; }
public Color TableSelectedRowBg { get; set; }
public Color TableSelectedRowHoverBg { get; set; }
@@ -227,8 +234,7 @@ internal class DataGridToken : AbstractControlDesignToken
public Color TableExpandIconBg { get; set; }
public double TableExpandColumnWidth { get; set; }
public Color TableExpandedRowBg { get; set; }
- public double TableFilterDropdownWidth { get; set; }
- public double TableFilterDropdownSearchWidth { get; set; }
+
public int ZIndexTableFixed { get; set; }
public int ZIndexTableSticky { get; set; }
@@ -338,10 +344,13 @@ internal class DataGridToken : AbstractControlDesignToken
TableExpandedRowBg = RowExpandedBg;
// Dropdown
- TableFilterDropdownWidth = 120;
- TableFilterDropdownHeight = 264;
- TableFilterDropdownSearchWidth = 140;
-
+ // TODO 暂未使用
+ TableFilterDropdownWidth = 120;
+ TableFilterDropdownHeight = 264;
+ TableFilterDropdownSearchWidth = 140;
+ TableFilterButtonSpacing = SharedToken.MarginXS;
+ TableFilterButtonContainerMargin = new Thickness(0, SharedToken.MarginXS, 0, SharedToken.MarginXXS);
+ TableFilterButtonLayoutSeparatorMargin = new Thickness(0, SharedToken.MarginXXS, 0, 0);
// Virtual Scroll Bar
TableScrollThumbSize = 8;
TableScrollThumbBg = StickyScrollBarBg;
diff --git a/src/AtomUI.Controls.DataGrid/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs b/src/AtomUI.Controls.DataGrid/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs
index 115da6637..e53972cb0 100644
--- a/src/AtomUI.Controls.DataGrid/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs
+++ b/src/AtomUI.Controls.DataGrid/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs
@@ -47,6 +47,9 @@ namespace AtomUI.Theme.Styling
public static readonly TokenResourceKey TableExpandColumnWidth = new TokenResourceKey("DataGrid.TableExpandColumnWidth");
public static readonly TokenResourceKey TableExpandedRowBg = new TokenResourceKey("DataGrid.TableExpandedRowBg");
public static readonly TokenResourceKey TableExpandIconBg = new TokenResourceKey("DataGrid.TableExpandIconBg");
+ public static readonly TokenResourceKey TableFilterButtonContainerMargin = new TokenResourceKey("DataGrid.TableFilterButtonContainerMargin");
+ public static readonly TokenResourceKey TableFilterButtonLayoutSeparatorMargin = new TokenResourceKey("DataGrid.TableFilterButtonLayoutSeparatorMargin");
+ public static readonly TokenResourceKey TableFilterButtonSpacing = new TokenResourceKey("DataGrid.TableFilterButtonSpacing");
public static readonly TokenResourceKey TableFilterDropdownBg = new TokenResourceKey("DataGrid.TableFilterDropdownBg");
public static readonly TokenResourceKey TableFilterDropdownHeight = new TokenResourceKey("DataGrid.TableFilterDropdownHeight");
public static readonly TokenResourceKey TableFilterDropdownSearchWidth = new TokenResourceKey("DataGrid.TableFilterDropdownSearchWidth");
diff --git a/src/AtomUI.Controls.DataGrid/Themes/DataGridColumnHeaderTheme.axaml b/src/AtomUI.Controls.DataGrid/Themes/DataGridColumnHeaderTheme.axaml
index 7bf8273fc..8509f8977 100644
--- a/src/AtomUI.Controls.DataGrid/Themes/DataGridColumnHeaderTheme.axaml
+++ b/src/AtomUI.Controls.DataGrid/Themes/DataGridColumnHeaderTheme.axaml
@@ -36,8 +36,15 @@
IsVisible="{TemplateBinding CanUserSort}" />
+ VerticalAlignment="Center">
+
+
+
+
+
+
+
+
diff --git a/src/AtomUI.Controls.DataGrid/DataGridThemeConstants.cs b/src/AtomUI.Controls.DataGrid/Themes/DataGridThemeConstants.cs
similarity index 94%
rename from src/AtomUI.Controls.DataGrid/DataGridThemeConstants.cs
rename to src/AtomUI.Controls.DataGrid/Themes/DataGridThemeConstants.cs
index f780ddacd..6fde6ff9e 100644
--- a/src/AtomUI.Controls.DataGrid/DataGridThemeConstants.cs
+++ b/src/AtomUI.Controls.DataGrid/Themes/DataGridThemeConstants.cs
@@ -114,4 +114,12 @@ internal static class DataGridThemeConstants
internal static class DataGridTopLeftColumnHeaderThemeConstants
{
+}
+
+internal static class DataGridMenuFilterFlyoutPresenterThemeConstants
+{
+ public const string ButtonLayoutPart = "PART_ButtonLayout";
+ public const string OkButtonPart = "PART_OkButton";
+ public const string ResetButtonPart = "PART_ResetButton";
+ public const string ButtonLayoutSeparatorPart = "PART_ButtonLayoutSeparator";
}
\ No newline at end of file
diff --git a/src/AtomUI.Controls.DataGrid/Themes/Filters/DataGridFilterMenuItemTheme.axaml b/src/AtomUI.Controls.DataGrid/Themes/Filters/DataGridFilterMenuItemTheme.axaml
new file mode 100644
index 000000000..ded50246f
--- /dev/null
+++ b/src/AtomUI.Controls.DataGrid/Themes/Filters/DataGridFilterMenuItemTheme.axaml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AtomUI.Controls.DataGrid/Themes/Filters/DataGridMenuFilterFlyoutPresenterTheme.axaml b/src/AtomUI.Controls.DataGrid/Themes/Filters/DataGridMenuFilterFlyoutPresenterTheme.axaml
new file mode 100644
index 000000000..c570dc87b
--- /dev/null
+++ b/src/AtomUI.Controls.DataGrid/Themes/Filters/DataGridMenuFilterFlyoutPresenterTheme.axaml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AtomUI.Controls.DataGrid/Utils/Converters/DataGridFilterIndicatorVisibleConvert.cs b/src/AtomUI.Controls.DataGrid/Utils/Converters/DataGridFilterIndicatorVisibleConvert.cs
index ac35a865a..49208b873 100644
--- a/src/AtomUI.Controls.DataGrid/Utils/Converters/DataGridFilterIndicatorVisibleConvert.cs
+++ b/src/AtomUI.Controls.DataGrid/Utils/Converters/DataGridFilterIndicatorVisibleConvert.cs
@@ -9,6 +9,13 @@ internal class DataGridFilterIndicatorVisibleConvert : IMultiValueConverter
public object Convert(IList