mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-29 07:16:33 +08:00
feature(Cascader): Add ItemsSource support
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using AtomUI.Controls;
|
||||
using AtomUI.Desktop.Controls;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace AtomUIGallery.ShowCases.ViewModels;
|
||||
@@ -12,6 +13,14 @@ public class CascaderViewModel : ReactiveObject, IRoutableViewModel, IActivatabl
|
||||
|
||||
public string UrlPathSegment { get; } = ID.ToString();
|
||||
|
||||
private List<ICascaderViewItemData> _basicCascaderNodes = [];
|
||||
|
||||
public List<ICascaderViewItemData> BasicCascaderNodes
|
||||
{
|
||||
get => _basicCascaderNodes;
|
||||
set => this.RaiseAndSetIfChanged(ref _basicCascaderNodes, value);
|
||||
}
|
||||
|
||||
public CascaderViewModel(IScreen screen)
|
||||
{
|
||||
HostScreen = screen;
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="AtomUIGallery.ShowCases.Views.CascaderShowCase"
|
||||
xmlns:atom="https://atomui.net"
|
||||
xmlns:viewModels="clr-namespace:AtomUIGallery.ShowCases.ViewModels"
|
||||
xmlns:gallery="https://atomui.net/oss-controls/gallery">
|
||||
<gallery:ShowCasePanel>
|
||||
<gallery:ShowCaseItem
|
||||
Title="Basic CascaderView"
|
||||
Description="The most basic usage."
|
||||
IsOccupyEntireRow="True">
|
||||
Description="The most basic usage.">
|
||||
<StackPanel Orientation="Vertical" Spacing="10">
|
||||
<atom:CascaderView>
|
||||
<atom:CascaderViewItem Header="Zhejiang">
|
||||
@@ -21,9 +21,23 @@
|
||||
</atom:CascaderViewItem>
|
||||
</atom:CascaderViewItem>
|
||||
</atom:CascaderView>
|
||||
|
||||
|
||||
<atom:CascaderView/>
|
||||
</StackPanel>
|
||||
</gallery:ShowCaseItem>
|
||||
|
||||
<gallery:ShowCaseItem
|
||||
Title="Generate by template"
|
||||
Description="You can use the Template mechanism to generate tree nodes">
|
||||
<atom:CascaderView
|
||||
x:DataType="viewModels:CascaderViewModel"
|
||||
ItemsSource="{Binding BasicCascaderNodes}">
|
||||
<atom:CascaderView.ItemTemplate>
|
||||
<TreeDataTemplate ItemsSource="{Binding Children}">
|
||||
<atom:TextBlock Text="{Binding Header}" />
|
||||
</TreeDataTemplate>
|
||||
</atom:CascaderView.ItemTemplate>
|
||||
</atom:CascaderView>
|
||||
</gallery:ShowCaseItem>
|
||||
</gallery:ShowCasePanel>
|
||||
</UserControl>
|
||||
@@ -1,3 +1,4 @@
|
||||
using AtomUI.Desktop.Controls;
|
||||
using AtomUIGallery.ShowCases.ViewModels;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
@@ -8,7 +9,57 @@ public partial class CascaderShowCase : ReactiveUserControl<CascaderViewModel>
|
||||
{
|
||||
public CascaderShowCase()
|
||||
{
|
||||
this.WhenActivated(disposables => { });
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
if (DataContext is CascaderViewModel vm)
|
||||
{
|
||||
InitBasicCascaderViewData(vm);
|
||||
}
|
||||
});
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitBasicCascaderViewData(CascaderViewModel viewModel)
|
||||
{
|
||||
viewModel.BasicCascaderNodes = [
|
||||
new CascaderViewItemData()
|
||||
{
|
||||
Header = "Zhejiang",
|
||||
Value = "zhejiang",
|
||||
Children = [
|
||||
new CascaderViewItemData()
|
||||
{
|
||||
Header = "Hangzhou",
|
||||
Value = "hangzhou",
|
||||
Children = [
|
||||
new CascaderViewItemData()
|
||||
{
|
||||
Header = "West Lake",
|
||||
Value = "xihu",
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
new CascaderViewItemData()
|
||||
{
|
||||
Header = "Jiangsu",
|
||||
Value = "jiangsu",
|
||||
Children = [
|
||||
new CascaderViewItemData()
|
||||
{
|
||||
Header = "Nanjing",
|
||||
Value = "nanjing",
|
||||
Children = [
|
||||
new CascaderViewItemData()
|
||||
{
|
||||
Header = "Zhong Hua Men",
|
||||
Value = "zhonghuamen",
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1059,32 +1059,78 @@ public partial class CascaderView : ItemsControl, IMotionAwareControl, IControlS
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 保证当前 Item 的父亲都在了
|
||||
var parents = new List<CascaderViewItem>();
|
||||
var parent = cascaderViewItem.Parent;
|
||||
while (parent != null && parent is CascaderViewItem parentCascaderItem)
|
||||
{
|
||||
parents.Add(parentCascaderItem);
|
||||
parent = parent.Parent;
|
||||
}
|
||||
|
||||
parents.Reverse();
|
||||
try
|
||||
{
|
||||
_ignoreExpandAndCollapse = true;
|
||||
foreach (var parentItem in parents)
|
||||
|
||||
if (cascaderViewItem.ItemsSource == null)
|
||||
{
|
||||
if (!parentItem.IsExpanded)
|
||||
// 保证当前 Item 的父亲都在了
|
||||
var parents = new List<CascaderViewItem>();
|
||||
var parent = cascaderViewItem.Parent;
|
||||
while (parent != null && parent is CascaderViewItem parentCascaderItem)
|
||||
{
|
||||
var parentList = new CascaderViewLevelList()
|
||||
parents.Add(parentCascaderItem);
|
||||
parent = parent.Parent;
|
||||
}
|
||||
|
||||
parents.Reverse();
|
||||
|
||||
foreach (var parentItem in parents)
|
||||
{
|
||||
if (!parentItem.IsExpanded)
|
||||
{
|
||||
ItemsPanel = ItemsPanel,
|
||||
};
|
||||
BindUtils.RelayBind(this, IsMotionEnabledProperty, parentList, CascaderViewLevelList.IsMotionEnabledProperty);
|
||||
parentList.ItemsSource = parentItem.Items;
|
||||
_itemsPanel.Children.Add(parentList);
|
||||
parentItem.IsExpanded = true;
|
||||
var parentList = new CascaderViewLevelList()
|
||||
{
|
||||
ItemsPanel = ItemsPanel,
|
||||
OwnerView = this
|
||||
};
|
||||
if (parentItem.ItemsSource == null)
|
||||
{
|
||||
foreach (var item in parentItem.Items)
|
||||
{
|
||||
parentList.Items.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
BindUtils.RelayBind(parentItem, CascaderViewItem.ItemsSourceProperty, parentList,
|
||||
CascaderViewLevelList.ItemsSourceProperty);
|
||||
BindUtils.RelayBind(parentItem, CascaderViewItem.ItemTemplateProperty, parentList,
|
||||
CascaderViewLevelList.ItemTemplateProperty);
|
||||
_itemsPanel.Children.Add(parentList);
|
||||
parentItem.IsExpanded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 保证当前 Item 的父亲都在了
|
||||
var parents = new List<ICascaderViewItemData>();
|
||||
var parent = cascaderViewItem.DataContext as ICascaderViewItemData;
|
||||
while (parent != null)
|
||||
{
|
||||
parents.Add(parent);
|
||||
parent = parent.ParentNode as ICascaderViewItemData;
|
||||
}
|
||||
parents.Reverse();
|
||||
foreach (var parentItem in parents)
|
||||
{
|
||||
if (!parentItem.IsExpanded)
|
||||
{
|
||||
var parentList = new CascaderViewLevelList()
|
||||
{
|
||||
ItemsPanel = ItemsPanel,
|
||||
OwnerView = this
|
||||
};
|
||||
|
||||
BindUtils.RelayBind(this, ItemTemplateProperty, parentList,
|
||||
CascaderViewLevelList.ItemTemplateProperty);
|
||||
_itemsPanel.Children.Add(parentList);
|
||||
if (parentItem is CascaderViewItemData cascaderViewItemData)
|
||||
{
|
||||
cascaderViewItemData.IsExpanded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1099,6 +1145,7 @@ public partial class CascaderView : ItemsControl, IMotionAwareControl, IControlS
|
||||
if (_itemsPanel.Children[count] is CascaderViewLevelList levelList)
|
||||
{
|
||||
levelList.ItemsSource = null;
|
||||
levelList.Items.Clear();
|
||||
}
|
||||
_itemsPanel.Children.RemoveAt(count);
|
||||
}
|
||||
@@ -1106,9 +1153,18 @@ public partial class CascaderView : ItemsControl, IMotionAwareControl, IControlS
|
||||
var childList = new CascaderViewLevelList()
|
||||
{
|
||||
ItemsPanel = ItemsPanel,
|
||||
OwnerView = this
|
||||
};
|
||||
BindUtils.RelayBind(this, IsMotionEnabledProperty, childList, CascaderViewLevelList.IsMotionEnabledProperty);
|
||||
childList.ItemsSource = cascaderViewItem.Items;
|
||||
|
||||
BindUtils.RelayBind(cascaderViewItem, CascaderViewItem.ItemsSourceProperty, childList, CascaderViewLevelList.ItemsSourceProperty);
|
||||
BindUtils.RelayBind(cascaderViewItem, CascaderViewItem.ItemTemplateProperty, childList, CascaderViewLevelList.ItemTemplateProperty);
|
||||
if (cascaderViewItem.ItemsSource == null)
|
||||
{
|
||||
foreach (var item in cascaderViewItem.Items)
|
||||
{
|
||||
childList.Items.Add(item);
|
||||
}
|
||||
}
|
||||
_itemsPanel.Children.Add(childList);
|
||||
InvalidateMeasure();
|
||||
}
|
||||
@@ -1138,6 +1194,7 @@ public partial class CascaderView : ItemsControl, IMotionAwareControl, IControlS
|
||||
if (_itemsPanel.Children[count] is CascaderViewLevelList levelList)
|
||||
{
|
||||
levelList.ItemsSource = null;
|
||||
levelList.Items.Clear();
|
||||
}
|
||||
|
||||
_itemsPanel.Children.RemoveAt(count);
|
||||
|
||||
@@ -321,7 +321,6 @@ public class CascaderViewItem : HeaderedItemsControl, IRadioButton, ICascaderVie
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly BorderRenderHelper _borderRenderHelper;
|
||||
private CascaderViewItemHeader? _header;
|
||||
private readonly Dictionary<CascaderViewItem, CompositeDisposable> _itemsBindingDisposables = new();
|
||||
internal bool AsyncLoaded;
|
||||
@@ -345,7 +344,6 @@ public class CascaderViewItem : HeaderedItemsControl, IRadioButton, ICascaderVie
|
||||
|
||||
public CascaderViewItem()
|
||||
{
|
||||
_borderRenderHelper = new BorderRenderHelper();
|
||||
LogicalChildren.CollectionChanged += HandleLogicalChildrenChanged;
|
||||
Items.CollectionChanged += HandleCollectionChanged;
|
||||
}
|
||||
@@ -383,7 +381,21 @@ public class CascaderViewItem : HeaderedItemsControl, IRadioButton, ICascaderVie
|
||||
{
|
||||
base.OnAttachedToLogicalTree(e);
|
||||
_cascaderView = this.GetLogicalAncestors().OfType<CascaderView>().FirstOrDefault();
|
||||
Level = CalculateDistanceFromLogicalParent<CascaderView>(this) - 1;
|
||||
if (DataContext is ICascaderViewItemData cascaderViewItemData)
|
||||
{
|
||||
var level = 0;
|
||||
ITreeNode<ICascaderViewItemData>? current = cascaderViewItemData;
|
||||
while (current != null)
|
||||
{
|
||||
level++;
|
||||
current = current.ParentNode;
|
||||
}
|
||||
Level = level - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Level = CalculateDistanceFromLogicalParent<CascaderView>(this) - 1;
|
||||
}
|
||||
if (ItemTemplate == null && _cascaderView?.ItemTemplate != null)
|
||||
{
|
||||
SetCurrentValue(ItemTemplateProperty, _cascaderView.ItemTemplate);
|
||||
@@ -611,21 +623,6 @@ public class CascaderViewItem : HeaderedItemsControl, IRadioButton, ICascaderVie
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Render(DrawingContext context)
|
||||
{
|
||||
if (_header != null)
|
||||
{
|
||||
using var state = context.PushTransform(Matrix.CreateTranslation(_header.Bounds.X, 0));
|
||||
_borderRenderHelper.Render(context,
|
||||
_header.Bounds.Size,
|
||||
new Thickness(),
|
||||
CornerRadius,
|
||||
BackgroundSizing.InnerBorderEdge,
|
||||
Background,
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
internal bool PointInHeaderBounds(PointerReleasedEventArgs e)
|
||||
{
|
||||
|
||||
@@ -1,25 +1,18 @@
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Reactive.Disposables;
|
||||
using AtomUI.Controls;
|
||||
using AtomUI.Data;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace AtomUI.Desktop.Controls;
|
||||
|
||||
internal class CascaderViewLevelList : ItemsControl, IMotionAwareControl
|
||||
internal class CascaderViewLevelList : ItemsControl
|
||||
{
|
||||
public static readonly StyledProperty<bool> IsMotionEnabledProperty =
|
||||
MotionAwareControlProperty.IsMotionEnabledProperty.AddOwner<CascaderViewLevelList>();
|
||||
|
||||
public bool IsMotionEnabled
|
||||
{
|
||||
get => GetValue(IsMotionEnabledProperty);
|
||||
set => SetValue(IsMotionEnabledProperty, value);
|
||||
}
|
||||
|
||||
private readonly Dictionary<CascaderViewItem, CompositeDisposable> _itemsBindingDisposables = new();
|
||||
|
||||
internal CascaderView? OwnerView { get; set; }
|
||||
|
||||
public CascaderViewLevelList()
|
||||
{
|
||||
LogicalChildren.CollectionChanged += HandleLogicalChildrenChanged;
|
||||
@@ -46,13 +39,44 @@ internal class CascaderViewLevelList : ItemsControl, IMotionAwareControl
|
||||
}
|
||||
}
|
||||
|
||||
protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
|
||||
{
|
||||
return new CascaderViewItem();
|
||||
}
|
||||
|
||||
protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
|
||||
{
|
||||
return NeedsContainer<CascaderViewItem>(item, out recycleKey);
|
||||
}
|
||||
|
||||
protected override void ContainerForItemPreparedOverride(Control container, object? item, int index)
|
||||
{
|
||||
base.ContainerForItemPreparedOverride(container, item, index);
|
||||
Debug.Assert(OwnerView != null);
|
||||
if (container is CascaderViewItem cascaderViewItem)
|
||||
{
|
||||
var disposables = new CompositeDisposable(2);
|
||||
disposables.Add(BindUtils.RelayBind(this, IsMotionEnabledProperty, cascaderViewItem, IsMotionEnabledProperty));
|
||||
|
||||
if (item != null && item is not Visual && item is ICascaderViewItemData cascaderViewItemData)
|
||||
{
|
||||
CascaderViewItem.ApplyNodeData(cascaderViewItem, cascaderViewItemData, disposables);
|
||||
}
|
||||
|
||||
if (ItemTemplate != null)
|
||||
{
|
||||
disposables.Add(BindUtils.RelayBind(this, ItemTemplateProperty, cascaderViewItem, CascaderViewItem.HeaderTemplateProperty));
|
||||
}
|
||||
|
||||
disposables.Add(BindUtils.RelayBind(OwnerView, CascaderView.ItemFilterActionProperty, cascaderViewItem, CascaderViewItem.ItemFilterActionProperty));
|
||||
disposables.Add(BindUtils.RelayBind(OwnerView, CascaderView.IsMotionEnabledProperty, cascaderViewItem, CascaderViewItem.IsMotionEnabledProperty));
|
||||
disposables.Add(BindUtils.RelayBind(OwnerView, CascaderView.IsShowIconProperty, cascaderViewItem, CascaderViewItem.IsShowIconProperty));
|
||||
disposables.Add(BindUtils.RelayBind(OwnerView, CascaderView.ToggleTypeProperty, cascaderViewItem, CascaderViewItem.ToggleTypeProperty));
|
||||
disposables.Add(BindUtils.RelayBind(OwnerView, CascaderView.FilterHighlightForegroundProperty, cascaderViewItem, CascaderViewItem.FilterHighlightForegroundProperty));
|
||||
disposables.Add(BindUtils.RelayBind(OwnerView, CascaderView.HasItemAsyncDataLoaderProperty, cascaderViewItem,
|
||||
CascaderViewItem.HasItemAsyncDataLoaderProperty));
|
||||
disposables.Add(BindUtils.RelayBind(OwnerView, CascaderView.IsAutoExpandParentProperty, cascaderViewItem,
|
||||
CascaderViewItem.IsAutoExpandParentProperty));
|
||||
|
||||
if (_itemsBindingDisposables.TryGetValue(cascaderViewItem, out var oldDisposables))
|
||||
{
|
||||
oldDisposables.Dispose();
|
||||
|
||||
Reference in New Issue
Block a user