diff --git a/src/AtomUI.Desktop.Controls/Cascader/Cascader.cs b/src/AtomUI.Desktop.Controls/Cascader/Cascader.cs index c82d4ac8b..ecdee06c8 100644 --- a/src/AtomUI.Desktop.Controls/Cascader/Cascader.cs +++ b/src/AtomUI.Desktop.Controls/Cascader/Cascader.cs @@ -244,6 +244,7 @@ public class Cascader : AbstractSelect private CascaderView? _cascaderView; private CompositeDisposable? _contentRightAddOnBindings; private bool _needSkipSyncSelectedOptions; + private bool _isDefaultSelectOptionPathApplied; static Cascader() { @@ -267,7 +268,42 @@ public class Cascader : AbstractSelect private void HandleCascaderSourceChanged(AvaloniaPropertyChangedEventArgs args) { + var selectedOptionPath = !IsMultiple ? BuildOptionIdentityPath(SelectedOption) : null; + var selectedOptionPaths = IsMultiple ? BuildSelectedOptionIdentityPaths(SelectedOptions) : null; + _options.SetItemsSource(args.GetNewValue?>()); + + if (!IsMultiple) + { + if (selectedOptionPath != null && + TryParseSelectPath(selectedOptionPath, out var selectedOptions) && + selectedOptions.Count > 0) + { + SelectedOption = selectedOptions[^1]; + _isDefaultSelectOptionPathApplied = false; + } + else + { + ApplyDefaultSelectOptionPath(_isDefaultSelectOptionPathApplied); + } + } + else if (selectedOptionPaths != null) + { + var remappedOptions = new List(selectedOptionPaths.Count); + foreach (var path in selectedOptionPaths) + { + if (TryParseSelectPath(path, out var selectedOptions) && + selectedOptions.Count > 0) + { + remappedOptions.Add(selectedOptions[^1]); + } + } + + if (remappedOptions.Count > 0) + { + SelectedOptions = remappedOptions; + } + } } private void HandleCascaderOptionsChanged(object? sender, NotifyCollectionChangedEventArgs args) @@ -294,6 +330,7 @@ public class Cascader : AbstractSelect public void Clear() { + _isDefaultSelectOptionPathApplied = false; SelectedOption = null; SelectedOptionPath = null; SelectedOptions = null; @@ -422,8 +459,20 @@ public class Cascader : AbstractSelect if (change.Property == SelectedOptionProperty) { + if (SelectedOption != null) + { + _isDefaultSelectOptionPathApplied = false; + } ConfigureSelectedOptionPath(); } + else if (change.Property == DefaultSelectOptionPathProperty) + { + _isDefaultSelectOptionPathApplied = false; + if (SelectedOption == null) + { + ApplyDefaultSelectOptionPath(true); + } + } } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) @@ -941,6 +990,82 @@ public class Cascader : AbstractSelect return options; } + private static List? BuildSelectedOptionIdentityPaths(ICollection? options) + { + if (options == null) + { + return null; + } + + var paths = new List(options.Count); + foreach (var option in options) + { + var path = BuildOptionIdentityPath(option); + if (path != null) + { + paths.Add(path); + } + } + return paths; + } + + private static TreeNodePath? BuildOptionIdentityPath(ICascaderOption? option) + { + if (option == null) + { + return null; + } + + var depth = CountOptionPathDepth(option); + var segments = new string[depth]; + var current = option; + for (var i = segments.Length - 1; current != null; i--) + { + var segment = current.ItemKey?.ToString() ?? current.Value?.ToString(); + if (string.IsNullOrEmpty(segment)) + { + return null; + } + + segments[i] = segment; + current = current.ParentNode as ICascaderOption; + } + + return new TreeNodePath(segments); + } + + private bool TryParseSelectPath(TreeNodePath path, out IList pathNodes) + { + var segments = path.Segments; + var isPathValid = true; + var currentItems = BuildOptionsList(Options); + var options = new List(segments.Count); + for (var i = 0; i < segments.Count; i++) + { + var segment = segments[i]; + var found = false; + foreach (var currentItem in currentItems) + { + if (segment == currentItem.ItemKey || segment == currentItem.Value?.ToString()) + { + options.Add(currentItem); + currentItems = BuildOptionsList(currentItem.Children); + found = true; + break; + } + } + + if (!found) + { + isPathValid = false; + break; + } + } + + pathNodes = options; + return isPathValid; + } + private static bool AreAllChildrenSelected( IEnumerable children, ISet selected) @@ -1010,24 +1135,35 @@ public class Cascader : AbstractSelect return count; } + private void ApplyDefaultSelectOptionPath(bool forceRefresh = false) + { + if (IsMultiple || DefaultSelectOptionPath == null) + { + return; + } + + if (!forceRefresh && (SelectedOption != null || SelectedOptionPath != null)) + { + return; + } + + if (TryParseSelectPath(DefaultSelectOptionPath, out var options) && + options.Count > 0) + { + var parts = new string[options.Count]; + for (var i = 0; i < options.Count; i++) + { + parts[i] = options[i].Header?.ToString() ?? string.Empty; + } + SetCurrentValue(SelectedOptionPathProperty, string.Join("/", parts)); + _isDefaultSelectOptionPathApplied = true; + } + } + protected override void OnLoaded(RoutedEventArgs e) { base.OnLoaded(e); - if (DefaultSelectOptionPath != null && SelectedOptionPath == null) - { - if (_cascaderView != null) - { - if (_cascaderView.TryParseSelectPath(DefaultSelectOptionPath, out var options)) - { - var parts = new string[options.Count]; - for (var i = 0; i < options.Count; i++) - { - parts[i] = options[i].Header?.ToString() ?? string.Empty; - } - SetCurrentValue(SelectedOptionPathProperty, string.Join("/", parts)); - } - } - } + ApplyDefaultSelectOptionPath(); } #region 实现 FormItem 接口 diff --git a/src/AtomUI.Desktop.Controls/Cascader/CascaderView.cs b/src/AtomUI.Desktop.Controls/Cascader/CascaderView.cs index 7d7fe954d..33845019f 100644 --- a/src/AtomUI.Desktop.Controls/Cascader/CascaderView.cs +++ b/src/AtomUI.Desktop.Controls/Cascader/CascaderView.cs @@ -459,7 +459,49 @@ public partial class CascaderView : TemplatedControl, private void HandleCascaderSourceChanged(AvaloniaPropertyChangedEventArgs args) { + ResetLevelListsForOptionsSourceChanging(); _options.SetItemsSource(args.GetNewValue?>()); + RestoreRootLevelListItemsSource(); + if (DefaultExpandedPath != null && IsLoaded) + { + _defaultExpandPathApplied = false; + ApplyDefaultExpandPath(); + _defaultExpandPathApplied = true; + } + } + + private void ResetLevelListsForOptionsSourceChanging() + { + if (_itemsPanel == null) + { + return; + } + + for (var i = _itemsPanel.Children.Count - 1; i >= 0; i--) + { + if (_itemsPanel.Children[i] is CascaderViewLevelList levelList) + { + levelList.ResetVirtualizingContext(); + levelList.SelectedIndex = -1; + levelList.SelectedItem = null; + levelList.ItemsSource = null; + if (i > 0) + { + levelList.Items.Clear(); + _itemsPanel.Children.RemoveAt(i); + } + } + } + } + + private void RestoreRootLevelListItemsSource() + { + if (_rootLevelList == null) + { + return; + } + + _rootLevelList.ItemsSource = Options; } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) diff --git a/src/AtomUI.Desktop.Controls/Cascader/CascaderViewLevelList.cs b/src/AtomUI.Desktop.Controls/Cascader/CascaderViewLevelList.cs index 9bda654f1..57911e7c4 100644 --- a/src/AtomUI.Desktop.Controls/Cascader/CascaderViewLevelList.cs +++ b/src/AtomUI.Desktop.Controls/Cascader/CascaderViewLevelList.cs @@ -78,18 +78,16 @@ internal class CascaderViewLevelList : SelectingItemsControl, IListVirtualizingC if (this is IListVirtualizingContextAware listVirtualizingContextAwareControl && cascaderViewItem is IListItemVirtualizingContextAware virtualListItem) { + if (item is ICascaderOption option) + { + NotifyRestoreDefaultContext(cascaderViewItem, option); + } + if (_virtualRestoreContext.TryGetValue(index, out var context)) { listVirtualizingContextAwareControl.RestoreVirtualizingContext(cascaderViewItem, context); _virtualRestoreContext.Remove(index); } - else - { - if (item is ICascaderOption option) - { - NotifyRestoreDefaultContext(cascaderViewItem, option); - } - } virtualListItem.VirtualIndex = index; } else @@ -245,6 +243,7 @@ internal class CascaderViewLevelList : SelectingItemsControl, IListVirtualizingC { item.SetCurrentValue(CascaderViewItem.HeaderProperty, option); item.ItemKey = option.ItemKey; + item.SetCurrentValue(CascaderViewItem.ValueProperty, option.Value); item.SetCurrentValue(CascaderViewItem.IconProperty, option.Icon); item.SetCurrentValue(CascaderViewItem.IsCheckedProperty, option.IsChecked); item.SetCurrentValue(CascaderViewItem.IsEnabledProperty, option.IsEnabled); @@ -311,6 +310,10 @@ internal class CascaderViewLevelList : SelectingItemsControl, IListVirtualizingC protected virtual void NotifyClearContainerForVirtualizingContext(CascaderViewItem item) { + item.ClearValue(CascaderViewItem.HeaderProperty); + item.ClearValue(CascaderViewItem.ValueProperty); + item.ItemKey = null; + item.ClearValue(CascaderViewItem.IconProperty); item.ClearValue(CascaderViewItem.IsEnabledProperty); item.ClearValue(CascaderViewItem.IsCheckedProperty); item.ClearValue(CascaderViewItem.IsExpandedProperty); @@ -318,6 +321,11 @@ internal class CascaderViewLevelList : SelectingItemsControl, IListVirtualizingC item.AsyncLoaded = false; } + internal void ResetVirtualizingContext() + { + _virtualRestoreContext.Clear(); + } + void IListVirtualizingContextAware.SaveVirtualizingContext(Control item, IDictionary context) { if (item is CascaderViewItem cascaderViewItem) diff --git a/src/AtomUI.Desktop.Controls/ListBox/ListBox.cs b/src/AtomUI.Desktop.Controls/ListBox/ListBox.cs index 8bfc9edd3..d67b96a3c 100644 --- a/src/AtomUI.Desktop.Controls/ListBox/ListBox.cs +++ b/src/AtomUI.Desktop.Controls/ListBox/ListBox.cs @@ -352,6 +352,10 @@ public class ListBox : AvaloniaListBox, { listBoxItem[!ListBoxItem.ContentTemplateProperty] = this[!ItemTemplateProperty]; } + else + { + listBoxItem.ClearValue(ListBoxItem.ContentTemplateProperty); + } listBoxItem[!ListBoxItem.SizeTypeProperty] = this[!SizeTypeProperty]; listBoxItem[!ListBoxItem.IsMotionEnabledProperty] = this[!IsMotionEnabledProperty]; listBoxItem[!ListBoxItem.SelectedIndicatorProperty] = this[!SelectedIndicatorProperty]; @@ -626,14 +630,13 @@ public class ListBox : AvaloniaListBox, protected virtual void NotifyRestoreDefaultContext(ListBoxItem item, IListItemData itemData) { - if (!item.IsSet(ListBoxItem.ContentProperty)) - { - item.SetCurrentValue(ListBoxItem.ContentProperty, itemData); - } + item.SetCurrentValue(ListBoxItem.ContentProperty, itemData); } protected virtual void NotifyClearContainerForVirtualizingContext(ListBoxItem item) { + item.ClearValue(ListBoxItem.ContentProperty); + item.ClearValue(ListBoxItem.ContentTemplateProperty); item.ClearValue(ListBoxItem.IsEnabledProperty); } diff --git a/src/AtomUI.Desktop.Controls/ListView/ListView.Virtualizing.cs b/src/AtomUI.Desktop.Controls/ListView/ListView.Virtualizing.cs index 334eec75e..8eb89cd5d 100644 --- a/src/AtomUI.Desktop.Controls/ListView/ListView.Virtualizing.cs +++ b/src/AtomUI.Desktop.Controls/ListView/ListView.Virtualizing.cs @@ -43,10 +43,7 @@ public partial class ListView : IListVirtualizingContextAware protected virtual void NotifyRestoreDefaultContext(ListViewItem item, IListItemData itemData) { - if (!item.IsSet(ListViewItem.ContentProperty)) - { - item.SetCurrentValue(ListViewItem.ContentProperty, itemData); - } + item.SetCurrentValue(ListViewItem.ContentProperty, itemData); if (itemData is IGroupListItemData groupListItemData) { item.SetCurrentValue(ListViewItem.IsGroupItemProperty, groupListItemData.IsGroupItem); @@ -56,6 +53,8 @@ public partial class ListView : IListVirtualizingContextAware protected virtual void NotifyClearContainerForVirtualizingContext(ListViewItem item) { + item.ClearValue(ListViewItem.ContentProperty); + item.ClearValue(ListViewItem.ContentTemplateProperty); item.ClearValue(ListViewItem.IsEnabledProperty); item.ClearValue(ListViewItem.IsGroupItemProperty); } diff --git a/src/AtomUI.Desktop.Controls/ListView/ListView.cs b/src/AtomUI.Desktop.Controls/ListView/ListView.cs index 24a219a09..df85a08fa 100644 --- a/src/AtomUI.Desktop.Controls/ListView/ListView.cs +++ b/src/AtomUI.Desktop.Controls/ListView/ListView.cs @@ -649,6 +649,14 @@ public partial class ListView : ItemsControl, ISizeTypeAware, IMotionAwareContro { listItem[!ListViewItem.ContentTemplateProperty] = this[!GroupItemTemplateProperty]; } + else if (ItemTemplate != null) + { + listItem[!ListViewItem.ContentTemplateProperty] = this[!ItemTemplateProperty]; + } + else + { + listItem.ClearValue(ListViewItem.ContentTemplateProperty); + } } else { @@ -656,6 +664,10 @@ public partial class ListView : ItemsControl, ISizeTypeAware, IMotionAwareContro { listItem[!ListViewItem.ContentTemplateProperty] = this[!ItemTemplateProperty]; } + else + { + listItem.ClearValue(ListViewItem.ContentTemplateProperty); + } } listItem[!ListViewItem.SizeTypeProperty] = this[!SizeTypeProperty]; diff --git a/src/AtomUI.Desktop.Controls/Select/Select.cs b/src/AtomUI.Desktop.Controls/Select/Select.cs index 42a394c73..62a14047c 100644 --- a/src/AtomUI.Desktop.Controls/Select/Select.cs +++ b/src/AtomUI.Desktop.Controls/Select/Select.cs @@ -989,12 +989,96 @@ public partial class Select : AbstractSelect private void HandleOptionsSourcePropertyChanged(AvaloniaPropertyChangedEventArgs change) { - ClearValue(); + var selectedOptionIdentity = Mode == SelectMode.Single ? BuildOptionIdentity(SelectedOption) : null; + var selectedOptionIdentities = Mode != SelectMode.Single ? BuildSelectedOptionIdentities(SelectedOptions) : null; + if (!Options.IsReadOnly) { Options.Clear(); } Options.SetItemsSource(change.GetNewValue?>()); + + if (Mode == SelectMode.Single) + { + if (selectedOptionIdentity != null && + TryFindOptionByIdentity(selectedOptionIdentity, out var remappedOption)) + { + SelectedOption = remappedOption; + } + else + { + SelectedOption = null; + ConfigureDefaultValues(); + } + } + else if (selectedOptionIdentities != null) + { + var remappedOptions = new List(selectedOptionIdentities.Count); + foreach (var identity in selectedOptionIdentities) + { + if (TryFindOptionByIdentity(identity, out var remappedOption)) + { + remappedOptions.Add(remappedOption); + } + } + + SelectedOptions = remappedOptions.Count > 0 ? remappedOptions : null; + ConfigureDefaultValues(); + } + else + { + ConfigureDefaultValues(); + } + } + + private static List? BuildSelectedOptionIdentities(ICollection? options) + { + if (options == null) + { + return null; + } + + var identities = new List(options.Count); + foreach (var option in options) + { + var identity = BuildOptionIdentity(option); + if (identity != null) + { + identities.Add(identity); + } + } + return identities; + } + + private static string? BuildOptionIdentity(ISelectOption? option) + { + if (option == null) + { + return null; + } + + var itemKey = option.ItemKey?.ToString(); + if (!string.IsNullOrEmpty(itemKey)) + { + return itemKey; + } + return option.Content?.ToString(); + } + + private bool TryFindOptionByIdentity(string identity, out ISelectOption option) + { + foreach (var item in Options) + { + if (item is ISelectOption currentOption && + identity == BuildOptionIdentity(currentOption)) + { + option = currentOption; + return true; + } + } + + option = null!; + return false; } private void ConfigureDefaultValues() diff --git a/src/AtomUI.Desktop.Controls/TreeSelect/TreeSelect.cs b/src/AtomUI.Desktop.Controls/TreeSelect/TreeSelect.cs index 89a30cabc..c833f8236 100644 --- a/src/AtomUI.Desktop.Controls/TreeSelect/TreeSelect.cs +++ b/src/AtomUI.Desktop.Controls/TreeSelect/TreeSelect.cs @@ -356,7 +356,38 @@ public class TreeSelect : AbstractSelect private void HandleItemsSourceChanged(AvaloniaPropertyChangedEventArgs args) { + var selectedItemPath = !IsMultiple ? BuildNodeIdentityPath(SelectedItem) : null; + var selectedItemPaths = IsMultiple ? BuildSelectedNodeIdentityPaths(SelectedItems) : null; + _items.SetItemsSource(args.GetNewValue?>()); + + if (!IsMultiple) + { + if (selectedItemPath != null && + TryParseNodePath(selectedItemPath, out var selectedItems) && + selectedItems.Count > 0) + { + SelectedItem = selectedItems[^1]; + } + else + { + SelectedItem = null; + } + } + else if (selectedItemPaths != null) + { + var remappedItems = new List(selectedItemPaths.Count); + foreach (var path in selectedItemPaths) + { + if (TryParseNodePath(path, out var selectedItems) && + selectedItems.Count > 0) + { + remappedItems.Add(selectedItems[^1]); + } + } + + SelectedItems = remappedItems.Count > 0 ? remappedItems : null; + } } private void HandleItemsChanged(object? sender, NotifyCollectionChangedEventArgs args) @@ -1153,6 +1184,95 @@ public class TreeSelect : AbstractSelect return items; } + private static List? BuildSelectedNodeIdentityPaths(ICollection? nodes) + { + if (nodes == null) + { + return null; + } + + var paths = new List(nodes.Count); + foreach (var node in nodes) + { + var path = BuildNodeIdentityPath(node); + if (path != null) + { + paths.Add(path); + } + } + return paths; + } + + private static TreeNodePath? BuildNodeIdentityPath(ITreeItemNode? node) + { + if (node == null) + { + return null; + } + + var depth = CountNodePathDepth(node); + var segments = new string[depth]; + var current = node; + for (var i = segments.Length - 1; current != null; i--) + { + var segment = current.ItemKey?.ToString() ?? current.Value?.ToString(); + if (string.IsNullOrEmpty(segment)) + { + return null; + } + + segments[i] = segment; + current = current.ParentNode as ITreeItemNode; + } + + return new TreeNodePath(segments); + } + + private bool TryParseNodePath(TreeNodePath path, out IList pathNodes) + { + var segments = path.Segments; + var isPathValid = true; + var currentItems = BuildTreeNodeList(Items); + var nodes = new List(segments.Count); + for (var i = 0; i < segments.Count; i++) + { + var segment = segments[i]; + var found = false; + foreach (var currentItem in currentItems) + { + if (segment == currentItem.ItemKey || segment == currentItem.Value?.ToString()) + { + nodes.Add(currentItem); + currentItems = BuildTreeNodeList(currentItem.Children); + found = true; + break; + } + } + + if (!found) + { + isPathValid = false; + break; + } + } + + pathNodes = nodes; + return isPathValid; + } + + private static int CountNodePathDepth(ITreeItemNode node) + { + var count = 0; + var current = node; + while (current != null) + { + count++; + current = current.ParentNode as ITreeItemNode; + } + + return count; + } + private static bool AreAllChildrenSelected( IEnumerable children, ISet selected) diff --git a/src/AtomUI.Desktop.Controls/TreeView/TreeView.cs b/src/AtomUI.Desktop.Controls/TreeView/TreeView.cs index 83729dd35..397c49a2e 100644 --- a/src/AtomUI.Desktop.Controls/TreeView/TreeView.cs +++ b/src/AtomUI.Desktop.Controls/TreeView/TreeView.cs @@ -1090,7 +1090,7 @@ public partial class TreeView : AvaloniaTreeView, { return null; } - if (treeViewItem.ItemKey != null && treeViewItem.ItemKey.Value == segment) + if (IsPathSegmentMatched(treeViewItem, segment)) { treeViewItem.SetCurrentValue(TreeViewItem.IsExpandedProperty, true); if (treeViewItem.Presenter?.Panel == null) @@ -1148,7 +1148,7 @@ public partial class TreeView : AvaloniaTreeView, { return null; } - if (treeViewItem.ItemKey != null && treeViewItem.ItemKey.Value == segment) + if (IsPathSegmentMatched(treeViewItem, segment)) { treeViewItem.SetCurrentValue(TreeViewItem.IsExpandedProperty, true); if (treeViewItem.Presenter?.Panel == null) @@ -1217,7 +1217,7 @@ public partial class TreeView : AvaloniaTreeView, return null; } - if (treeViewItem.ItemKey != null && treeViewItem.ItemKey.Value == segment) + if (IsPathSegmentMatched(treeViewItem, segment)) { pathNodeExpandStatus.Add(treeViewItem.IsExpanded); treeViewItem.SetCurrentValue(TreeViewItem.IsExpandedProperty, true); @@ -1262,6 +1262,15 @@ public partial class TreeView : AvaloniaTreeView, SetCurrentValue(IsMotionEnabledProperty, originIsMotionEnabled); } } + + private static bool IsPathSegmentMatched(TreeViewItem treeViewItem, string segment) + { + if (treeViewItem.ItemKey != null && treeViewItem.ItemKey.Value == segment) + { + return true; + } + return treeViewItem.Value?.ToString() == segment; + } private (ISet, ISet) SetupParentNodeCheckedStatus(TreeViewItem viewItem) { @@ -1314,6 +1323,147 @@ public partial class TreeView : AvaloniaTreeView, return (checkedParents, unCheckedParents); } + private void ConfigureStateAfterItemsSourceChanged() + { + if (!IsLoaded) + { + return; + } + + var selectedItemPath = BuildNodeIdentityPath(SelectedItem as ITreeItemNode); + var selectedItemPaths = BuildNodeIdentityPaths(SelectedItems); + var checkedItemPaths = BuildNodeIdentityPaths(CheckedItems); + + SetCurrentValue(SelectedItemProperty, null); + SelectedItems.Clear(); + CheckedItems.Clear(); + + var selectionRestored = false; + if (selectedItemPath != null) + { + selectionRestored = TrySelectNodePath(selectedItemPath); + } + if (!selectionRestored && selectedItemPaths != null) + { + foreach (var path in selectedItemPaths) + { + selectionRestored |= TrySelectNodePath(path); + } + } + if (!selectionRestored) + { + ConfigureDefaultSelectedPaths(); + } + + var checkedRestored = false; + if (checkedItemPaths != null) + { + foreach (var path in checkedItemPaths) + { + checkedRestored |= TryCheckNodePath(path); + } + } + if (!checkedRestored) + { + ConfigureDefaultCheckedPaths(); + } + + if (IsDefaultExpandAll) + { + ExpandAll(false); + } + else + { + ConfigureDefaultExpandedPaths(); + } + } + + private static List? BuildNodeIdentityPaths(IList? nodes) + { + if (nodes == null) + { + return null; + } + + var paths = new List(nodes.Count); + foreach (var node in nodes) + { + if (node is ITreeItemNode treeItemNode) + { + var path = BuildNodeIdentityPath(treeItemNode); + if (path != null) + { + paths.Add(path); + } + } + } + return paths; + } + + private static TreeNodePath? BuildNodeIdentityPath(ITreeItemNode? node) + { + if (node == null) + { + return null; + } + + var depth = CountTreeItemPathDepth(node); + var segments = new string[depth]; + var current = node; + for (var i = segments.Length - 1; current != null; i--) + { + var segment = current.ItemKey?.ToString() ?? current.Value?.ToString(); + if (string.IsNullOrEmpty(segment)) + { + return null; + } + + segments[i] = segment; + current = current.ParentNode as ITreeItemNode; + } + + return new TreeNodePath(segments); + } + + private bool TrySelectNodePath(TreeNodePath path) + { + var selected = false; + TraverTreeViewPath(path, (treeViewItem, i) => + { + if (i == path.Length - 1) + { + var item = TreeItemFromContainer(treeViewItem); + if (item != null) + { + if (!SelectedItems.Contains(item)) + { + SelectedItems.Add(item); + } + if (SelectionMode == SelectionMode.Single) + { + SetCurrentValue(SelectedItemProperty, item); + } + selected = true; + } + } + }); + return selected; + } + + private bool TryCheckNodePath(TreeNodePath path) + { + var isChecked = false; + TraverTreeViewPath(path, (treeViewItem, i) => + { + if (i == path.Length - 1) + { + treeViewItem.SetCurrentValue(TreeViewItem.IsCheckedProperty, true); + isChecked = true; + } + }); + return isChecked; + } + private void GetChildCheckStatus(TreeViewItem parentTreeItem, out bool isAllChecked, out bool isAnyChecked) { isAllChecked = false; @@ -1490,6 +1640,11 @@ public partial class TreeView : AvaloniaTreeView, SelectedItems.Clear(); } } + + if (change.Property == ItemsSourceProperty) + { + Dispatcher.Post(ConfigureStateAfterItemsSourceChanged); + } } protected void HandleSwitcherRotationIconChanged() diff --git a/src/AtomUI.Desktop.Controls/TreeView/TreeViewItem.cs b/src/AtomUI.Desktop.Controls/TreeView/TreeViewItem.cs index 9f21b6bf2..543d72989 100644 --- a/src/AtomUI.Desktop.Controls/TreeView/TreeViewItem.cs +++ b/src/AtomUI.Desktop.Controls/TreeView/TreeViewItem.cs @@ -847,6 +847,7 @@ public class TreeViewItem : AvaloniaTreeItem, IRadioButton, ITreeItemNode treeViewItem.SetCurrentValue(IsEnabledProperty, treeItemData.IsEnabled); treeViewItem.SetCurrentValue(IsExpandedProperty, treeItemData.IsExpanded); treeViewItem.SetCurrentValue(IsIndicatorEnabledProperty, treeItemData.IsIndicatorEnabled); + treeViewItem.SetCurrentValue(ValueProperty, treeItemData.Value); treeViewItem.ItemKey = treeItemData.ItemKey; treeViewItem.IsLeaf = treeItemData.IsLeaf;