Refactor NodeSwitcherButton

This commit is contained in:
chinboy
2025-01-24 11:14:12 +08:00
parent 05aea115c0
commit 4aeae4040c
6 changed files with 476 additions and 166 deletions
@@ -50,7 +50,7 @@ internal class ToggleIconButtonTheme : BaseControlTheme
enabledStyle.Add(iconStyle);
}
var hoverStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Selected));
var hoverStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.PointerOver));
{
var iconStyle = new Style(selector => selector.Nesting().Template().OfType<Icon>());
iconStyle.Add(Icon.IconModeProperty, IconMode.Active);
@@ -1,25 +1,34 @@
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.Controls.Utils;
using AtomUI.IconPkg;
using AtomUI.IconPkg.AntDesign;
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Media;
namespace AtomUI.Controls;
/// <summary>
/// 如果 checked icon 和 unchecked icon 都设置的时候,就直接切换
/// 否则就直接改变 render transform
/// </summary>
internal class NodeSwitcherButton : ToggleIconButton
internal enum NodeSwitcherButtonIconMode
{
Default,
Rotation,
Leaf,
Loading
}
internal class NodeSwitcherButton : ToggleButton
{
#region 公共属性
public static readonly StyledProperty<Icon?> ExpandIconProperty
= AvaloniaProperty.Register<NodeSwitcherButton, Icon?>(nameof(ExpandIcon));
public static readonly StyledProperty<Icon?> CollapseIconProperty
= AvaloniaProperty.Register<NodeSwitcherButton, Icon?>(nameof(CollapseIcon));
public static readonly StyledProperty<Icon?> LoadingIconProperty
= AvaloniaProperty.Register<NodeSwitcherButton, Icon?>(nameof(LoadingIcon));
@@ -29,6 +38,12 @@ internal class NodeSwitcherButton : ToggleIconButton
public static readonly StyledProperty<bool> IsLeafProperty
= AvaloniaProperty.Register<NodeSwitcherButton, bool>(nameof(IsLeaf));
public static readonly StyledProperty<bool> IsLoadingProperty
= AvaloniaProperty.Register<NodeSwitcherButton, bool>(nameof(IsLoading), false);
public static readonly StyledProperty<Icon?> RotationIconProperty
= AvaloniaProperty.Register<NodeSwitcherButton, Icon?>(nameof(RotationIcon));
public Icon? LoadingIcon
{
@@ -47,18 +62,82 @@ internal class NodeSwitcherButton : ToggleIconButton
get => GetValue(IsLeafProperty);
set => SetValue(IsLeafProperty, value);
}
public bool IsLoading
{
get => GetValue(IsLoadingProperty);
set => SetValue(IsLoadingProperty, value);
}
public Icon? RotationIcon
{
get => GetValue(RotationIconProperty);
set => SetValue(RotationIconProperty, value);
}
public Icon? ExpandIcon
{
get => GetValue(ExpandIconProperty);
set => SetValue(ExpandIconProperty, value);
}
public Icon? CollapseIcon
{
get => GetValue(CollapseIconProperty);
set => SetValue(CollapseIconProperty, value);
}
#endregion
#region 内部属性定义
internal static readonly StyledProperty<bool> IsIconVisibleProperty
= AvaloniaProperty.Register<NodeSwitcherButton, bool>(nameof(IsIconVisible), true);
internal static readonly StyledProperty<bool> IsLeafIconVisibleProperty
= AvaloniaProperty.Register<NodeSwitcherButton, bool>(nameof(IsLeafIconVisible), true);
internal static readonly DirectProperty<NodeSwitcherButton, NodeSwitcherButtonIconMode> IconModeProperty =
AvaloniaProperty.RegisterDirect<NodeSwitcherButton, NodeSwitcherButtonIconMode>(
nameof(IconMode),
o => o.IconMode,
(o, v) => o.IconMode = v);
internal static readonly DirectProperty<NodeSwitcherButton, bool> ExpandIconVisibleProperty =
AvaloniaProperty.RegisterDirect<NodeSwitcherButton, bool>(
nameof(ExpandIconVisible),
o => o.ExpandIconVisible,
(o, v) => o.ExpandIconVisible = v);
internal static readonly DirectProperty<NodeSwitcherButton, bool> CollapseIconVisibleProperty =
AvaloniaProperty.RegisterDirect<NodeSwitcherButton, bool>(
nameof(CollapseIconVisible),
o => o.CollapseIconVisible,
(o, v) => o.CollapseIconVisible = v);
internal bool IsIconVisible
internal bool IsLeafIconVisible
{
get => GetValue(IsIconVisibleProperty);
set => SetValue(IsIconVisibleProperty, value);
get => GetValue(IsLeafIconVisibleProperty);
set => SetValue(IsLeafIconVisibleProperty, value);
}
private NodeSwitcherButtonIconMode _iconMode;
internal NodeSwitcherButtonIconMode IconMode
{
get => _iconMode;
set => SetAndRaise(IconModeProperty, ref _iconMode, value);
}
private bool _expandIconVisible;
internal bool ExpandIconVisible
{
get => _expandIconVisible;
set => SetAndRaise(ExpandIconVisibleProperty, ref _expandIconVisible, value);
}
private bool _collapseIconVisible;
internal bool CollapseIconVisible
{
get => _collapseIconVisible;
set => SetAndRaise(CollapseIconVisibleProperty, ref _collapseIconVisible, value);
}
#endregion
@@ -67,119 +146,73 @@ internal class NodeSwitcherButton : ToggleIconButton
static NodeSwitcherButton()
{
AffectsMeasure<NodeSwitcherButton>(LoadingIconProperty, LeafIconProperty);
AffectsRender<NodeSwitcherButton>(BackgroundProperty);
AffectsMeasure<NodeSwitcherButton>(ExpandIconProperty, CollapseIconProperty, IsCheckedProperty,
LoadingIconProperty, LeafIconProperty);
}
public NodeSwitcherButton()
{
SetCurrentValue(CursorProperty, new Cursor(StandardCursorType.Hand));
_borderRenderHelper = new BorderRenderHelper();
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
LoadingIcon ??= AntDesignIconPackage.LoadingOutlined();
ConfigureFixedSizeIcon(LoadingIcon);
LoadingIcon.LoadingAnimation = IconAnimation.Spin;
LeafIcon ??= AntDesignIconPackage.FileOutlined();
ConfigureFixedSizeIcon(LeafIcon);
SetupIconVisibility(IsChecked ?? false);
base.OnApplyTemplate(e);
ApplyIconToContent();
Transitions ??= new Transitions
{
AnimationUtils.CreateTransition<SolidColorBrushTransition>(BackgroundProperty)
};
}
private void ConfigureFixedSizeIcon(Icon icon)
{
icon.SetCurrentValue(HorizontalAlignmentProperty, HorizontalAlignment.Center);
icon.SetCurrentValue(VerticalAlignmentProperty, VerticalAlignment.Center);
UIStructureUtils.SetTemplateParent(icon, this);
TokenResourceBinder.CreateGlobalResourceBinding(icon, WidthProperty, GlobalTokenResourceKey.IconSize);
TokenResourceBinder.CreateGlobalResourceBinding(icon, HeightProperty, GlobalTokenResourceKey.IconSize);
BindUtils.RelayBind(this, IsIconVisibleProperty, icon, IsVisibleProperty);
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (VisualRoot is not null)
if (change.Property == CollapseIconProperty ||
change.Property == ExpandIconProperty ||
change.Property == LoadingIconProperty ||
change.Property == RotationIconProperty)
{
if (change.Property == IsLeafProperty)
if (change.NewValue is Icon icon)
{
ApplyIconToContent();
icon.HorizontalAlignment = HorizontalAlignment.Center;
icon.VerticalAlignment = VerticalAlignment.Center;
}
}
if (change.Property == LoadingIconProperty ||
change.Property == LeafIconProperty)
else if (change.Property == IsCheckedProperty)
{
if (change.NewValue is Icon newIcon)
{
ConfigureFixedSizeIcon(newIcon);
ApplyIconToContent();
RenderTransform = null;
}
}
if (change.Property == CheckedIconProperty ||
change.Property == UnCheckedIconProperty)
{
RenderTransform = null;
ApplyIconToContent();
var isChecked = (bool?)change.NewValue ?? false;
SetupIconVisibility(isChecked);
}
}
internal override void ApplyIconToContent()
private void SetupIconVisibility(bool isChecked)
{
if (!IsLeaf)
if (_iconMode != NodeSwitcherButtonIconMode.Default)
{
if (IsChecked.HasValue)
{
if (CheckedIcon is not null && UnCheckedIcon is not null)
{
// 直接切换模式
if (IsChecked.Value)
{
Content = CheckedIcon;
}
else
{
Content = UnCheckedIcon;
}
}
else if (UnCheckedIcon is not null)
{
// 通过 render transform 进行设置
Content = UnCheckedIcon;
if (IsChecked.Value)
{
RenderTransform = new RotateTransform(90);
}
else
{
RenderTransform = null;
}
}
}
else
{
Content = LoadingIcon;
}
ExpandIconVisible = false;
CollapseIconVisible = false;
}
else
{
RenderTransform = null;
Content = LeafIcon;
if (isChecked)
{
ExpandIconVisible = false;
CollapseIconVisible = true;
}
else
{
ExpandIconVisible = true;
CollapseIconVisible = false;
}
}
}
public override void Render(DrawingContext context)
{
if (IsIconVisible && !IsLeaf)
if (_iconMode != NodeSwitcherButtonIconMode.Leaf)
{
_borderRenderHelper.Render(context,
Bounds.Size,
@@ -1,28 +1,181 @@
using AtomUI.Theme.Styling;
using AtomUI.IconPkg;
using AtomUI.IconPkg.AntDesign;
using AtomUI.Theme;
using AtomUI.Theme.Styling;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Styling;
namespace AtomUI.Controls;
[ControlThemeProvider]
internal class NodeSwitcherButtonTheme : ToggleIconButtonTheme
internal class NodeSwitcherButtonTheme : BaseControlTheme
{
public const string RootPart = "PART_Root";
public const string RotationIconPart = "PART_RotationIcon";
public const string ExpandIconPart = "PART_ExpandIcon";
public const string CollapseIconPart = "PART_CollapseIcon";
public const string LoadingIconPart = "PART_LoadingIcon";
public const string LeafIconPart = "PART_LeafIcon";
public NodeSwitcherButtonTheme()
: base(typeof(NodeSwitcherButton))
{
}
protected override IControlTemplate BuildControlTemplate()
{
return new FuncControlTemplate<NodeSwitcherButton>((button, scope) =>
{
var rootContainer = new Panel
{
Name = RootPart,
IsHitTestVisible = false
};
rootContainer.RegisterInNameScope(scope);
// 我们需要在这个切换按钮显示几种状态的图标
// 1、展开和收缩两种状态的图标
// 2、只设置一个图标,展开和收缩通过旋转开表示
// 3、当节点是叶子节点的时候,没有展开和收缩的概念,直接显示叶子节点的图标
// 4、加载状态的图标
var expandIconPresenter = new ContentPresenter
{
Name = ExpandIconPart,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalContentAlignment = VerticalAlignment.Center,
};
expandIconPresenter.RegisterInNameScope(scope);
CreateTemplateParentBinding(expandIconPresenter, ContentPresenter.ContentProperty,
NodeSwitcherButton.ExpandIconProperty);
CreateTemplateParentBinding(expandIconPresenter, ContentPresenter.IsVisibleProperty,
NodeSwitcherButton.ExpandIconVisibleProperty);
var collapseIconPresenter = new ContentPresenter
{
Name = CollapseIconPart,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalContentAlignment = VerticalAlignment.Center,
};
collapseIconPresenter.RegisterInNameScope(scope);
CreateTemplateParentBinding(collapseIconPresenter, ContentPresenter.ContentProperty,
NodeSwitcherButton.CollapseIconProperty);
CreateTemplateParentBinding(collapseIconPresenter, ContentPresenter.IsVisibleProperty,
NodeSwitcherButton.CollapseIconVisibleProperty);
var rotationIconPresenter = new ContentPresenter
{
Name = RotationIconPart,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalContentAlignment = VerticalAlignment.Center,
};
rotationIconPresenter.RegisterInNameScope(scope);
CreateTemplateParentBinding(rotationIconPresenter, ContentPresenter.ContentProperty,
NodeSwitcherButton.RotationIconProperty);
CreateTemplateParentBinding(rotationIconPresenter, ContentPresenter.IsVisibleProperty,
NodeSwitcherButton.IconModeProperty, BindingMode.Default, new FuncValueConverter<NodeSwitcherButtonIconMode, bool>(iconMode =>
{
return iconMode == NodeSwitcherButtonIconMode.Rotation;
}));
var loadingIconPresenter = new ContentPresenter
{
Name = LoadingIconPart,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalContentAlignment = VerticalAlignment.Center,
};
loadingIconPresenter.RegisterInNameScope(scope);
CreateTemplateParentBinding(loadingIconPresenter, ContentPresenter.ContentProperty,
NodeSwitcherButton.LoadingIconProperty);
CreateTemplateParentBinding(loadingIconPresenter, ContentPresenter.IsVisibleProperty,
NodeSwitcherButton.IconModeProperty, BindingMode.Default, new FuncValueConverter<NodeSwitcherButtonIconMode, bool>(iconMode =>
{
return iconMode == NodeSwitcherButtonIconMode.Loading;
}));
var leafIconPresenter = new ContentPresenter
{
Name = LeafIconPart,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalContentAlignment = VerticalAlignment.Center,
};
leafIconPresenter.RegisterInNameScope(scope);
CreateTemplateParentBinding(leafIconPresenter, ContentPresenter.ContentProperty,
NodeSwitcherButton.LeafIconProperty);
CreateTemplateParentBinding(leafIconPresenter, ContentPresenter.IsVisibleProperty,
NodeSwitcherButton.IsLeafIconVisibleProperty);
rootContainer.Children.Add(expandIconPresenter);
rootContainer.Children.Add(collapseIconPresenter);
rootContainer.Children.Add(rotationIconPresenter);
rootContainer.Children.Add(loadingIconPresenter);
rootContainer.Children.Add(leafIconPresenter);
return rootContainer;
});
}
protected override void BuildStyles()
{
base.BuildStyles();
var commonStyle = new Style(selector => selector.Nesting());
commonStyle.Add(TemplatedControl.BackgroundProperty, GlobalTokenResourceKey.ColorTransparent);
commonStyle.Add(TemplatedControl.CornerRadiusProperty, GlobalTokenResourceKey.BorderRadius);
var hoverStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.PointerOver));
hoverStyle.Add(TemplatedControl.BackgroundProperty, TreeViewTokenResourceKey.NodeHoverBg);
commonStyle.Add(hoverStyle);
var checkStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Checked)
.PropertyEquals(NodeSwitcherButton.IconModeProperty, NodeSwitcherButtonIconMode.Rotation));
checkStyle.Add(NodeSwitcherButton.RenderTransformProperty, new RotateTransform(90));
commonStyle.Add(checkStyle);
Add(commonStyle);
var enabledStyle =
new Style(selector => selector.Nesting().PropertyEquals(InputElement.IsEnabledProperty, true));
{
{
var iconStyle = new Style(selector => selector.Nesting().Template().OfType<Icon>());
iconStyle.Add(Icon.IconModeProperty, IconMode.Normal);
enabledStyle.Add(iconStyle);
}
var hoverStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.PointerOver));
{
var iconStyle = new Style(selector => selector.Nesting().Template().OfType<Icon>());
iconStyle.Add(Icon.IconModeProperty, IconMode.Active);
hoverStyle.Add(iconStyle);
}
hoverStyle.Add(TemplatedControl.BackgroundProperty, TreeViewTokenResourceKey.NodeHoverBg);
enabledStyle.Add(hoverStyle);
var checkedStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Selected));
{
var iconStyle = new Style(selector => selector.Nesting().Template().OfType<Icon>());
iconStyle.Add(Icon.IconModeProperty, IconMode.Selected);
checkedStyle.Add(iconStyle);
}
enabledStyle.Add(checkedStyle);
}
Add(enabledStyle);
var disabledStyle =
new Style(selector => selector.Nesting().PropertyEquals(InputElement.IsEnabledProperty, false));
{
var iconStyle = new Style(selector => selector.Nesting().Template().OfType<Icon>());
iconStyle.Add(Icon.IconModeProperty, IconMode.Disabled);
disabledStyle.Add(iconStyle);
}
Add(disabledStyle);
}
}
+7 -7
View File
@@ -46,8 +46,8 @@ public class TreeView : AvaloniaTreeView
public static readonly StyledProperty<TreeItemHoverMode> NodeHoverModeProperty =
AvaloniaProperty.Register<TreeView, TreeItemHoverMode>(nameof(NodeHoverMode));
public static readonly StyledProperty<bool> IsShowLeafSwitcherProperty =
AvaloniaProperty.Register<TreeView, bool>(nameof(IsShowLeafSwitcher));
public static readonly StyledProperty<bool> IsShowLeafIconProperty =
AvaloniaProperty.Register<TreeView, bool>(nameof(IsShowLeafIcon));
public bool IsDraggable
{
@@ -79,10 +79,10 @@ public class TreeView : AvaloniaTreeView
set => SetValue(NodeHoverModeProperty, value);
}
public bool IsShowLeafSwitcher
public bool IsShowLeafIcon
{
get => GetValue(IsShowLeafSwitcherProperty);
set => SetValue(IsShowLeafSwitcherProperty, value);
get => GetValue(IsShowLeafIconProperty);
set => SetValue(IsShowLeafIconProperty, value);
}
public bool IsDefaultExpandAll { get; set; } = false;
@@ -274,8 +274,8 @@ public class TreeView : AvaloniaTreeView
BindUtils.RelayBind(this, NodeHoverModeProperty, treeViewItem, TreeViewItem.NodeHoverModeProperty);
BindUtils.RelayBind(this, IsShowLineProperty, treeViewItem, TreeViewItem.IsShowLineProperty);
BindUtils.RelayBind(this, IsShowIconProperty, treeViewItem, TreeViewItem.IsShowIconProperty);
BindUtils.RelayBind(this, IsShowLeafSwitcherProperty, treeViewItem,
TreeViewItem.IsShowLeafSwitcherProperty);
BindUtils.RelayBind(this, IsShowLeafIconProperty, treeViewItem,
TreeViewItem.IsShowLeafIconProperty);
BindUtils.RelayBind(this, IsCheckableProperty, treeViewItem, TreeViewItem.IsCheckboxVisibleProperty);
}
}
+169 -55
View File
@@ -39,11 +39,26 @@ public class TreeViewItem : AvaloniaTreeItem
public static readonly StyledProperty<Icon?> SwitcherCollapseIconProperty
= AvaloniaProperty.Register<TreeViewItem, Icon?>(nameof(SwitcherCollapseIcon));
public static readonly StyledProperty<Icon?> SwitcherRotationIconProperty
= AvaloniaProperty.Register<TreeViewItem, Icon?>(nameof(SwitcherRotationIcon));
public static readonly StyledProperty<Icon?> SwitcherLoadingIconProperty
= AvaloniaProperty.Register<TreeViewItem, Icon?>(nameof(SwitcherRotationIcon));
public static readonly StyledProperty<Icon?> SwitcherLeafIconProperty
= AvaloniaProperty.Register<TreeViewItem, Icon?>(nameof(SwitcherLeafIcon));
public static readonly DirectProperty<TreeViewItem, bool> IsLeafProperty
= AvaloniaProperty.RegisterDirect<TreeViewItem, bool>(nameof(IsLeaf),
o => o.IsLeaf,
(o, v) => o.IsLeaf = v);
public static readonly StyledProperty<bool> IsShowLeafIconProperty =
AvaloniaProperty.Register<TreeViewItem, bool>(nameof(IsShowLeafIcon));
public static readonly StyledProperty<bool> IsLoadingProperty
= AvaloniaProperty.Register<TreeViewItem, bool>(nameof(IsLoading), false);
public bool IsCheckable
{
@@ -69,6 +84,30 @@ public class TreeViewItem : AvaloniaTreeItem
set => SetValue(SwitcherCollapseIconProperty, value);
}
public Icon? SwitcherRotationIcon
{
get => GetValue(SwitcherRotationIconProperty);
set => SetValue(SwitcherRotationIconProperty, value);
}
public Icon? SwitcherLoadingIcon
{
get => GetValue(SwitcherLoadingIconProperty);
set => SetValue(SwitcherLoadingIconProperty, value);
}
public Icon? SwitcherLeafIcon
{
get => GetValue(SwitcherLeafIconProperty);
set => SetValue(SwitcherLeafIconProperty, value);
}
internal bool IsShowLeafIcon
{
get => GetValue(IsShowLeafIconProperty);
set => SetValue(IsShowLeafIconProperty, value);
}
public bool? IsChecked
{
get => GetValue(IsCheckedProperty);
@@ -82,6 +121,12 @@ public class TreeViewItem : AvaloniaTreeItem
get => _isLeaf;
internal set => SetAndRaise(IsLeafProperty, ref _isLeaf, value);
}
public bool IsLoading
{
get => GetValue(IsLoadingProperty);
set => SetValue(IsLoadingProperty, value);
}
public TreeNodeKey? Key { get; set; }
@@ -114,11 +159,6 @@ public class TreeViewItem : AvaloniaTreeItem
o => o.IconEffectiveVisible,
(o, v) => o.IconEffectiveVisible = v);
internal static readonly DirectProperty<TreeViewItem, bool> IsShowLeafSwitcherProperty =
AvaloniaProperty.RegisterDirect<TreeViewItem, bool>(nameof(IsShowLeafSwitcher),
o => o.IsShowLeafSwitcher,
(o, v) => o.IsShowLeafSwitcher = v);
internal static readonly DirectProperty<TreeViewItem, bool> IsCheckboxVisibleProperty =
AvaloniaProperty.RegisterDirect<TreeViewItem, bool>(nameof(IsCheckboxVisible),
o => o.IsCheckboxVisible,
@@ -190,14 +230,6 @@ public class TreeViewItem : AvaloniaTreeItem
set => SetAndRaise(IconEffectiveVisibleProperty, ref _iconEffectiveVisible, value);
}
private bool _isShowLeafSwitcher;
internal bool IsShowLeafSwitcher
{
get => _isShowLeafSwitcher;
set => SetAndRaise(IsShowLeafSwitcherProperty, ref _isShowLeafSwitcher, value);
}
private bool _isCheckboxVisible;
internal bool IsCheckboxVisible
@@ -267,7 +299,7 @@ public class TreeViewItem : AvaloniaTreeItem
AffectsRender<TreeViewItem>(EffectiveNodeCornerRadiusProperty,
EffectiveNodeBgProperty,
IsShowLineProperty,
IsShowLeafSwitcherProperty,
IsShowLeafIconProperty,
IsDraggingProperty,
IsDragOverProperty);
}
@@ -289,7 +321,10 @@ public class TreeViewItem : AvaloniaTreeItem
// 注册到 TreeView
OwnerTreeView?.DefaultCheckedItems.Add(this);
}
CreateNodeSwitcherDefaultIcons();
SetNodeSwitcherIcons();
SetupSwitcherButtonIconMode();
_initialized = true;
}
}
@@ -303,11 +338,12 @@ public class TreeViewItem : AvaloniaTreeItem
{
CalculateEffectiveBgRect();
}
else if (change.Property == IsShowLineProperty ||
change.Property == SwitcherExpandIconProperty ||
change.Property == SwitcherCollapseIconProperty)
else if (change.Property == SwitcherExpandIconProperty ||
change.Property == SwitcherCollapseIconProperty ||
change.Property == SwitcherRotationIconProperty)
{
SetNodeSwitcherIcons();
SetupSwitcherButtonIconMode();
}
else if (change.Property == IsEnabledProperty ||
change.Property == IsCheckableProperty)
@@ -328,6 +364,11 @@ public class TreeViewItem : AvaloniaTreeItem
OwnerTreeView?.UnCheckedSubTree(this);
}
}
}
else if (change.Property == IsShowLineProperty)
{
CreateNodeSwitcherDefaultIcons();
SetNodeSwitcherIcons();
}
}
@@ -351,6 +392,74 @@ public class TreeViewItem : AvaloniaTreeItem
{
UIStructureUtils.SetTemplateParent(newIcon, this);
}
}
else if (change.Property == IsLoadingProperty || change.Property == IsLeafProperty)
{
SetupSwitcherButtonIconMode();
}
}
private void SetupSwitcherButtonIconMode()
{
if (_switcherButton is not null)
{
if (!IsLeaf)
{
if (IsLoading)
{
_switcherButton.IconMode = NodeSwitcherButtonIconMode.Loading;
}
else
{
if (SwitcherExpandIcon != null && SwitcherCollapseIcon != null)
{
_switcherButton.IconMode = NodeSwitcherButtonIconMode.Default;
}
else if (SwitcherRotationIcon != null)
{
_switcherButton.IconMode = NodeSwitcherButtonIconMode.Rotation;
}
}
}
else
{
_switcherButton.IconMode = NodeSwitcherButtonIconMode.Leaf;
}
}
}
private void CreateNodeSwitcherDefaultIcons()
{
if (IsShowLine)
{
if (SwitcherExpandIcon == null)
{
SetValue(SwitcherExpandIconProperty, AntDesignIconPackage.PlusSquareOutlined(), BindingPriority.Template);
}
if (SwitcherCollapseIcon == null)
{
SetValue(SwitcherCollapseIconProperty, AntDesignIconPackage.MinusSquareOutlined(), BindingPriority.Template);
}
}
else
{
if (SwitcherRotationIcon == null)
{
SetValue(SwitcherRotationIconProperty, AntDesignIconPackage.CaretRightOutlined(), BindingPriority.Template);
}
}
if (SwitcherLoadingIcon == null)
{
var loadingIcon = AntDesignIconPackage.LoadingOutlined();
loadingIcon.LoadingAnimation = IconAnimation.Spin;
SetValue(SwitcherLoadingIconProperty, loadingIcon, BindingPriority.Template);
}
if (SwitcherLeafIcon == null)
{
SetValue(SwitcherLeafIconProperty, AntDesignIconPackage.FileOutlined(), BindingPriority.Template);
}
}
@@ -367,12 +476,15 @@ public class TreeViewItem : AvaloniaTreeItem
GlobalTokenResourceKey.BorderThickness,
BindingPriority.Template,
new RenderScaleAwareThicknessConfigure(this));
base.OnApplyTemplate(e);
SetNodeSwitcherIcons();
_headerPresenter = e.NameScope.Find<ContentPresenter>(TreeViewItemTheme.HeaderPresenterPart);
_iconPresenter = e.NameScope.Find<ContentPresenter>(TreeViewItemTheme.IconPresenterPart);
_frameDecorator = e.NameScope.Find<Border>(TreeViewItemTheme.FrameDecoratorPart);
_switcherButton = e.NameScope.Find<NodeSwitcherButton>(TreeViewItemTheme.NodeSwitcherButtonPart);
SetupSwitcherButtonIconMode();
if (_frameDecorator is not null)
{
@@ -393,7 +505,6 @@ public class TreeViewItem : AvaloniaTreeItem
}
IsLeaf = ItemCount == 0;
SetNodeSwitcherIcons();
SetupCheckBoxEnabled();
Transitions ??= new Transitions
@@ -463,8 +574,17 @@ public class TreeViewItem : AvaloniaTreeItem
{
EdgeMode = EdgeMode.Aliased
});
var isLastChild = false;
if (Parent is ItemsControl parentTreeItem)
{
if (parentTreeItem.ContainerFromIndex(parentTreeItem.ItemCount - 1) == this)
{
isLastChild = true;
}
}
if (!IsLeaf)
if (!IsLeaf && !isLastChild)
{
var switcherMiddleBottom =
_switcherButton.TranslatePoint(
@@ -477,17 +597,8 @@ public class TreeViewItem : AvaloniaTreeItem
}
// 画孩子线条
if (!IsShowLeafSwitcher && IsLeaf)
if (!IsShowLeafIcon && IsLeaf)
{
var isLastChild = false;
if (Parent is TreeViewItem parentTreeItem)
{
if (parentTreeItem.ContainerFromIndex(parentTreeItem.ItemCount - 1) == this)
{
isLastChild = true;
}
}
{
// 纵向
var childStartPoint =
@@ -525,39 +636,44 @@ public class TreeViewItem : AvaloniaTreeItem
private void SetNodeSwitcherIcons()
{
if (_switcherButton is null)
if (SwitcherExpandIcon is not null)
{
return;
TokenResourceBinder.CreateGlobalResourceBinding(SwitcherExpandIcon, WidthProperty,
GlobalTokenResourceKey.IconSize);
TokenResourceBinder.CreateGlobalResourceBinding(SwitcherExpandIcon, HeightProperty,
GlobalTokenResourceKey.IconSize);
}
if (SwitcherExpandIcon is not null || SwitcherCollapseIcon is not null)
if (SwitcherCollapseIcon is not null)
{
_switcherButton.UnCheckedIcon = SwitcherExpandIcon;
_switcherButton.CheckedIcon = SwitcherCollapseIcon;
TokenResourceBinder.CreateTokenBinding(_switcherButton, ToggleIconButton.IconWidthProperty,
TokenResourceBinder.CreateGlobalResourceBinding(SwitcherCollapseIcon, WidthProperty,
GlobalTokenResourceKey.IconSize);
TokenResourceBinder.CreateTokenBinding(_switcherButton, ToggleIconButton.IconHeightProperty,
TokenResourceBinder.CreateGlobalResourceBinding(SwitcherCollapseIcon, HeightProperty,
GlobalTokenResourceKey.IconSize);
return;
}
if (IsShowLine)
if (SwitcherRotationIcon is not null)
{
_switcherButton.UnCheckedIcon = AntDesignIconPackage.PlusSquareOutlined();
_switcherButton.CheckedIcon = AntDesignIconPackage.MinusSquareOutlined();
TokenResourceBinder.CreateTokenBinding(_switcherButton, ToggleIconButton.IconWidthProperty,
TokenResourceBinder.CreateGlobalResourceBinding(SwitcherRotationIcon, WidthProperty,
GlobalTokenResourceKey.IconSizeXS);
TokenResourceBinder.CreateGlobalResourceBinding(SwitcherRotationIcon, HeightProperty,
GlobalTokenResourceKey.IconSizeXS);
}
if (SwitcherLoadingIcon is not null)
{
TokenResourceBinder.CreateGlobalResourceBinding(SwitcherLoadingIcon, WidthProperty,
GlobalTokenResourceKey.IconSize);
TokenResourceBinder.CreateTokenBinding(_switcherButton, ToggleIconButton.IconHeightProperty,
TokenResourceBinder.CreateGlobalResourceBinding(SwitcherLoadingIcon, HeightProperty,
GlobalTokenResourceKey.IconSize);
}
else
if (SwitcherLeafIcon is not null)
{
_switcherButton.CheckedIcon = null;
_switcherButton.UnCheckedIcon = AntDesignIconPackage.CaretRightOutlined();
TokenResourceBinder.CreateTokenBinding(_switcherButton, ToggleIconButton.IconWidthProperty,
GlobalTokenResourceKey.IconSizeXS);
TokenResourceBinder.CreateTokenBinding(_switcherButton, ToggleIconButton.IconHeightProperty,
GlobalTokenResourceKey.IconSizeXS);
TokenResourceBinder.CreateGlobalResourceBinding(SwitcherLeafIcon, WidthProperty,
GlobalTokenResourceKey.IconSize);
TokenResourceBinder.CreateGlobalResourceBinding(SwitcherLeafIcon, HeightProperty,
GlobalTokenResourceKey.IconSize);
}
}
@@ -642,7 +758,7 @@ public class TreeViewItem : AvaloniaTreeItem
}
}
if (_switcherButton is not null && _switcherButton.IsIconVisible)
if (_switcherButton is not null && _switcherButton.IsLeafIconVisible)
{
offsetX -= _switcherButton.Bounds.Width;
}
@@ -681,6 +797,4 @@ public class TreeViewItem : AvaloniaTreeItem
{
return new DragPreviewAdorner(_frameDecorator!);
}
// #endregion
}
@@ -74,14 +74,22 @@ internal class TreeViewItemTheme : BaseControlTheme
Focusable = false
};
nodeSwitcherButton.RegisterInNameScope(scope);
CreateTemplateParentBinding(nodeSwitcherButton, ToggleIconButton.CheckedIconProperty,
CreateTemplateParentBinding(nodeSwitcherButton, NodeSwitcherButton.CollapseIconProperty,
TreeViewItem.SwitcherCollapseIconProperty);
CreateTemplateParentBinding(nodeSwitcherButton, ToggleIconButton.UnCheckedIconProperty,
CreateTemplateParentBinding(nodeSwitcherButton, NodeSwitcherButton.ExpandIconProperty,
TreeViewItem.SwitcherExpandIconProperty);
CreateTemplateParentBinding(nodeSwitcherButton, NodeSwitcherButton.RotationIconProperty,
TreeViewItem.SwitcherRotationIconProperty);
CreateTemplateParentBinding(nodeSwitcherButton, NodeSwitcherButton.LoadingIconProperty,
TreeViewItem.SwitcherLoadingIconProperty);
CreateTemplateParentBinding(nodeSwitcherButton, NodeSwitcherButton.LeafIconProperty,
TreeViewItem.SwitcherLeafIconProperty);
CreateTemplateParentBinding(nodeSwitcherButton, ToggleButton.IsCheckedProperty,
Avalonia.Controls.TreeViewItem.IsExpandedProperty, BindingMode.TwoWay);
CreateTemplateParentBinding(nodeSwitcherButton, NodeSwitcherButton.IsLeafProperty,
TreeViewItem.IsLeafProperty);
CreateTemplateParentBinding(nodeSwitcherButton, NodeSwitcherButton.IsLoadingProperty,
TreeViewItem.IsLoadingProperty);
treeItemLayout.Children.Add(nodeSwitcherButton);
Grid.SetColumn(nodeSwitcherButton, 0);
@@ -241,13 +249,15 @@ internal class TreeViewItemTheme : BaseControlTheme
}
Add(leafSwitcherButtonStyle);
var leafAndHideButtonStyle = new Style(selector => selector
.Nesting().PropertyEquals(TreeViewItem.IsLeafProperty, true)
.PropertyEquals(TreeViewItem.IsShowLeafSwitcherProperty,
false));
var leafAndHideButtonStyle = new Style(selector => Selectors.Or(selector
.Nesting().PropertyEquals(TreeViewItem.IsLeafProperty, true)
.PropertyEquals(TreeViewItem.IsShowLeafIconProperty,
false),
selector
.Nesting().PropertyEquals(TreeViewItem.IsLeafProperty, false)));
{
var switcherButtonStyle = new Style(selector => selector.Nesting().Template().Name(NodeSwitcherButtonPart));
switcherButtonStyle.Add(NodeSwitcherButton.IsIconVisibleProperty, false);
switcherButtonStyle.Add(NodeSwitcherButton.IsLeafIconVisibleProperty, false);
leafAndHideButtonStyle.Add(switcherButtonStyle);
}
Add(leafAndHideButtonStyle);