mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-28 23:08:36 +08:00
Refactor NodeSwitcherButton
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user