mirror of
https://gitee.com/ant-design-blazor/ant-design-blazor.git
synced 2024-12-05 13:37:35 +08:00
600c8595dc
* feat(module: tree): Add 'Checkable' to TreeNode. * feat(module: tree): Remove the dependency of 'Uncheckable' on 'CheckStrictly'.
1014 lines
30 KiB
C#
1014 lines
30 KiB
C#
// Licensed to the .NET Foundation under one or more agreements.
|
|
// The .NET Foundation licenses this file to you under the MIT license.
|
|
// See the LICENSE file in the project root for more information.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Components;
|
|
using Microsoft.AspNetCore.Components.Web;
|
|
|
|
namespace AntDesign
|
|
{
|
|
public partial class TreeNode<TItem> : AntDomComponentBase
|
|
{
|
|
#region Node
|
|
|
|
/// <summary>
|
|
/// 树控件本身
|
|
/// </summary>
|
|
[CascadingParameter(Name = "Tree")]
|
|
public Tree<TItem> TreeComponent { get; set; }
|
|
|
|
/// <summary>
|
|
/// 上一级节点
|
|
/// </summary>
|
|
[CascadingParameter(Name = "Node")]
|
|
public TreeNode<TItem> ParentNode { get; set; }
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
[Parameter]
|
|
public RenderFragment Nodes { get; set; }
|
|
|
|
[Parameter]
|
|
public RenderFragment ChildContent { get; set; }
|
|
|
|
internal List<TreeNode<TItem>> ChildNodes { get; set; } = new List<TreeNode<TItem>>();
|
|
|
|
/// <summary>
|
|
/// Whether child nodes exist
|
|
/// </summary>
|
|
internal bool HasChildNodes => ChildNodes?.Count > 0;
|
|
|
|
/// <summary>
|
|
/// Current Node Level
|
|
/// </summary>
|
|
public int TreeLevel => (ParentNode?.TreeLevel ?? -1) + 1;
|
|
|
|
/// <summary>
|
|
/// record the index in children nodes list of parent node.
|
|
/// </summary>
|
|
internal int NodeIndex { get; set; }
|
|
|
|
/// <summary>
|
|
/// Determine if it is the last node in the same level nodes.
|
|
/// </summary>
|
|
internal bool IsLastNode => NodeIndex == (ParentNode?.ChildNodes.Count ?? TreeComponent?.ChildNodes.Count) - 1;
|
|
|
|
/// <summary>
|
|
/// add node to parent node
|
|
/// </summary>
|
|
/// <param name="treeNode"></param>
|
|
internal void AddNode(TreeNode<TItem> treeNode)
|
|
{
|
|
treeNode.NodeIndex = ChildNodes.Count;
|
|
ChildNodes.Add(treeNode);
|
|
IsLeaf = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find a node
|
|
/// </summary>
|
|
/// <param name="predicate">Predicate</param>
|
|
/// <param name="recursive">Recursive Find</param>
|
|
/// <returns></returns>
|
|
public TreeNode<TItem> FindFirstOrDefaultNode(Func<TreeNode<TItem>, bool> predicate, bool recursive = true)
|
|
{
|
|
foreach (var child in ChildNodes)
|
|
{
|
|
if (predicate.Invoke(child))
|
|
{
|
|
return child;
|
|
}
|
|
if (recursive)
|
|
{
|
|
var find = child.FindFirstOrDefaultNode(predicate, recursive);
|
|
if (find != null)
|
|
{
|
|
return find;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Obtain the parent data set
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public List<TreeNode<TItem>> GetParentNodes()
|
|
{
|
|
if (ParentNode != null)
|
|
return ParentNode.ChildNodes;
|
|
else
|
|
return TreeComponent.ChildNodes;
|
|
}
|
|
|
|
public TreeNode<TItem> GetPreviousNode()
|
|
{
|
|
var parentNodes = GetParentNodes();
|
|
var index = parentNodes.IndexOf(this);
|
|
if (index == 0) return null;
|
|
else return parentNodes[index - 1];
|
|
}
|
|
|
|
public TreeNode<TItem> GetNextNode()
|
|
{
|
|
var parentNodes = GetParentNodes();
|
|
var index = parentNodes.IndexOf(this);
|
|
if (index == parentNodes.Count - 1) return null;
|
|
else return parentNodes[index + 1];
|
|
}
|
|
|
|
#endregion Node
|
|
|
|
#region TreeNode
|
|
|
|
private static long _nextNodeId;
|
|
|
|
internal long NodeId { get; private set; }
|
|
|
|
public TreeNode()
|
|
{
|
|
NodeId = Interlocked.Increment(ref _nextNodeId);
|
|
}
|
|
|
|
private string _key;
|
|
|
|
/// <summary>
|
|
/// Specifies the unique identifier name of the current node。
|
|
/// </summary>
|
|
[Parameter]
|
|
public string Key
|
|
{
|
|
get
|
|
{
|
|
if (TreeComponent.KeyExpression != null)
|
|
return TreeComponent.KeyExpression(this);
|
|
else
|
|
return _key;
|
|
}
|
|
set
|
|
{
|
|
_key = value;
|
|
}
|
|
}
|
|
|
|
private bool _disabled;
|
|
|
|
/// <summary>
|
|
/// The disabled state is subject to the parent node
|
|
/// </summary>
|
|
[Parameter]
|
|
public bool Disabled
|
|
{
|
|
get { return _disabled || (ParentNode?.Disabled ?? false); }
|
|
set { _disabled = value; }
|
|
}
|
|
|
|
private bool _actualSelected;
|
|
|
|
private bool _selected;
|
|
|
|
/// <summary>
|
|
/// Selected or not
|
|
/// </summary>
|
|
[Parameter]
|
|
public bool Selected
|
|
{
|
|
get => _actualSelected;
|
|
set => _selected = value;
|
|
}
|
|
|
|
[Parameter]
|
|
public EventCallback<bool> SelectedChanged { get; set; }
|
|
|
|
/// <summary>
|
|
/// Setting Selection State
|
|
/// </summary>
|
|
/// <param name="value"></param>
|
|
public void SetSelected(bool value)
|
|
{
|
|
if (!TreeComponent.Selectable) return;
|
|
DoSelect(value, false, true);
|
|
TreeComponent.UpdateSelectedKeys();
|
|
}
|
|
|
|
internal void DoSelect(bool value, bool isMulti, bool isManual)
|
|
{
|
|
if (Disabled && !TreeComponent.Multiple)
|
|
{
|
|
_actualSelected = false;
|
|
}
|
|
else
|
|
{
|
|
value = (!Disabled || !isManual) ? value : _actualSelected;
|
|
if (_actualSelected == value) return;
|
|
if (value == true)
|
|
{
|
|
if (!(TreeComponent.Multiple && (TreeComponent.IsCtrlKeyDown || isMulti)))
|
|
{
|
|
TreeComponent.DoDeselectAll(isManual);
|
|
}
|
|
TreeComponent.TriggerOnSelect(this);
|
|
}
|
|
else
|
|
{
|
|
TreeComponent.TriggerOnUnselect(this);
|
|
}
|
|
_actualSelected = value;
|
|
}
|
|
if (SelectedChanged.HasDelegate)
|
|
SelectedChanged.InvokeAsync(_actualSelected);
|
|
StateHasChanged();
|
|
return;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether the load state is asynchronous (affects the display of the expansion icon)
|
|
/// </summary>
|
|
[Parameter]
|
|
public bool Loading { get; set; }
|
|
|
|
private bool _dragTarget;
|
|
|
|
/// <summary>
|
|
/// Whether or not to release the target
|
|
/// </summary>
|
|
internal bool DragTarget
|
|
{
|
|
get { return _dragTarget; }
|
|
set
|
|
{
|
|
_dragTarget = value;
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
internal bool DragTargetBottom { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Sets the node to release the target location
|
|
/// </summary>
|
|
/// <param name="value"></param>
|
|
public void SetTargetBottom(bool value = false)
|
|
{
|
|
if (DragTargetBottom == value) return;
|
|
DragTargetBottom = value;
|
|
StateHasChanged();
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
private bool TargetContainer { get; set; }
|
|
|
|
/// <summary>
|
|
/// Sets the drag and drop target node container
|
|
/// </summary>
|
|
internal void SetParentTargetContainer(bool value = false)
|
|
{
|
|
if (ParentNode == null) return;
|
|
if (ParentNode.TargetContainer == value) return;
|
|
ParentNode.TargetContainer = value;
|
|
ParentNode.StateHasChanged();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the children of the parent node
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private List<TreeNode<TItem>> GetParentChildNodes()
|
|
{
|
|
return ParentNode?.ChildNodes ?? TreeComponent.ChildNodes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove the current node
|
|
/// </summary>
|
|
public void RemoveNode()
|
|
{
|
|
GetParentChildNodes().Remove(this);
|
|
}
|
|
|
|
private void SetTreeNodeClassMapper()
|
|
{
|
|
ClassMapper
|
|
.Add("ant-tree-treenode")
|
|
.If("ant-tree-treenode-disabled", () => Disabled)
|
|
.If("ant-tree-treenode-switcher-open", () => SwitcherOpen)
|
|
.If("ant-tree-treenode-switcher-close", () => SwitcherClose)
|
|
.If("ant-tree-treenode-checkbox-checked", () => Checked)
|
|
.If("ant-tree-treenode-checkbox-indeterminate", () => Indeterminate)
|
|
.If("ant-tree-treenode-selected", () => Selected)
|
|
.If("ant-tree-treenode-loading", () => Loading)
|
|
.If("drop-target", () => DragTarget)
|
|
.If("drag-over-gap-bottom", () => DragTarget && DragTargetBottom)
|
|
.If("drag-over", () => DragTarget && !DragTargetBottom)
|
|
.If("drop-container", () => TargetContainer)
|
|
.If("ant-tree-treenode-leaf-last", () => IsLastNode);
|
|
}
|
|
|
|
#endregion TreeNode
|
|
|
|
#region Switcher
|
|
|
|
private bool _isLeaf = true;
|
|
|
|
/// <summary>
|
|
/// Whether it is a leaf node
|
|
/// </summary>
|
|
[Parameter]
|
|
public bool IsLeaf
|
|
{
|
|
get
|
|
{
|
|
if (TreeComponent.IsLeafExpression != null)
|
|
return TreeComponent.IsLeafExpression(this);
|
|
else
|
|
return _isLeaf;
|
|
}
|
|
set
|
|
{
|
|
if (_isLeaf == value) return;
|
|
_isLeaf = value;
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Expand the node or not
|
|
/// </summary>
|
|
[Parameter]
|
|
public bool Expanded
|
|
{
|
|
get => _actualExpanded;
|
|
set => _expanded = value;
|
|
}
|
|
|
|
private bool _actualExpanded = false;
|
|
|
|
private bool _expanded = false;
|
|
|
|
[Parameter]
|
|
public EventCallback<bool> ExpandedChanged { get; set; }
|
|
|
|
/// <summary>
|
|
/// Expand the node
|
|
/// </summary>
|
|
/// <param name="expanded"></param>
|
|
public async Task Expand(bool expanded)
|
|
{
|
|
await DoExpand(expanded);
|
|
await TreeComponent?.UpdateExpandedKeys();
|
|
}
|
|
|
|
internal async Task DoExpand(bool expanded)
|
|
{
|
|
if (_actualExpanded == expanded)
|
|
{
|
|
return;
|
|
}
|
|
_actualExpanded = expanded;
|
|
if (ExpandedChanged.HasDelegate)
|
|
{
|
|
await ExpandedChanged.InvokeAsync(_actualExpanded);
|
|
}
|
|
await TreeComponent?.OnNodeExpand(this, _actualExpanded, new MouseEventArgs());
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Expand all child nodes
|
|
/// </summary>
|
|
internal async Task ExpandAll()
|
|
{
|
|
await SwitchAllNodes(this, true);
|
|
await TreeComponent?.UpdateExpandedKeys();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Collapse all child nodes
|
|
/// </summary>
|
|
internal async Task CollapseAll()
|
|
{
|
|
await SwitchAllNodes(this, false);
|
|
await TreeComponent?.UpdateExpandedKeys();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 节点展开关闭
|
|
/// </summary>
|
|
/// <param name="node"></param>
|
|
/// <param name="expanded"></param>
|
|
private async Task SwitchAllNodes(TreeNode<TItem> node, bool expanded)
|
|
{
|
|
await node.DoExpand(expanded);
|
|
node.ChildNodes.ForEach(n => _ = SwitchAllNodes(n, expanded));
|
|
}
|
|
|
|
/// <summary>
|
|
/// The real expand state, as long as there is a expaneded node on the path, then all the folds below
|
|
/// </summary>
|
|
internal bool RealDisplay
|
|
{
|
|
get
|
|
{
|
|
if (Hidden) return false;
|
|
if (ParentNode == null) return true;
|
|
if (ParentNode.Expanded == false) return false;
|
|
return ParentNode.RealDisplay;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Nodes switch
|
|
/// </summary>
|
|
/// <param name="args"></param>
|
|
/// <returns></returns>
|
|
private void OnSwitcherClick(MouseEventArgs args)
|
|
{
|
|
_ = Expand(!_actualExpanded);
|
|
}
|
|
|
|
internal void SetLoading(bool loading)
|
|
{
|
|
Loading = loading;
|
|
}
|
|
|
|
/// <summary>
|
|
/// switcher is opened
|
|
/// </summary>
|
|
private bool SwitcherOpen => Expanded && !IsLeaf;
|
|
|
|
/// <summary>
|
|
/// switcher is close
|
|
/// </summary>
|
|
private bool SwitcherClose => !Expanded && !IsLeaf;
|
|
|
|
/// <summary>
|
|
/// expaned parents
|
|
/// </summary>
|
|
internal void OpenPropagation(bool unhide = false)
|
|
{
|
|
_ = DoExpand(true);
|
|
if (unhide)
|
|
Hidden = false;
|
|
if (ParentNode != null)
|
|
ParentNode.OpenPropagation(unhide);
|
|
}
|
|
|
|
#endregion Switcher
|
|
|
|
#region Checkbox
|
|
|
|
/// <summary>
|
|
/// Check the TreeNode or not
|
|
/// </summary>
|
|
[Parameter]
|
|
public bool Checked
|
|
{
|
|
get
|
|
{
|
|
return _actualChecked;
|
|
}
|
|
set
|
|
{
|
|
_checked = value;
|
|
}
|
|
}
|
|
|
|
private bool _actualChecked = false;
|
|
|
|
private bool _checked = false;
|
|
|
|
[Parameter]
|
|
public EventCallback<bool> CheckedChanged { get; set; }
|
|
|
|
private bool _checkable = true;
|
|
|
|
[Parameter]
|
|
public bool Checkable
|
|
{
|
|
get => TreeComponent.Checkable && _checkable;
|
|
set => _checkable = value;
|
|
}
|
|
|
|
public bool Indeterminate { get; set; }
|
|
|
|
private bool _disableCheckbox;
|
|
|
|
/// <summary>
|
|
/// Disable checkbox
|
|
/// </summary>
|
|
[Parameter]
|
|
public bool DisableCheckbox
|
|
{
|
|
get
|
|
{
|
|
return _disableCheckbox || (TreeComponent?.DisableCheckKeys?.Any(k => k == Key) ?? false);
|
|
}
|
|
set
|
|
{
|
|
_disableCheckbox = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Triggered when the selection box is clicked
|
|
/// </summary>
|
|
private async void OnCheckBoxClick(MouseEventArgs args)
|
|
{
|
|
if (Disabled || DisableCheckbox)
|
|
return;
|
|
SetChecked(!_actualChecked);
|
|
if (TreeComponent.OnCheck.HasDelegate)
|
|
await TreeComponent.OnCheck.InvokeAsync(new TreeEventArgs<TItem>(TreeComponent, this, args));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the checkbox state
|
|
/// </summary>
|
|
/// <param name="check"></param>
|
|
public void SetChecked(bool check)
|
|
{
|
|
if (!DoCheck(check, false, true)) return;
|
|
TreeComponent.UpdateCheckedKeys();
|
|
}
|
|
|
|
internal bool DoCheck(bool check, bool strict, bool isManual)
|
|
{
|
|
if (!Checkable) return false;
|
|
else if (TreeComponent.CheckStrictly || strict)
|
|
{
|
|
_actualChecked = (!Disabled || !DisableCheckbox || !isManual) ? check : _actualChecked;
|
|
Indeterminate = false;
|
|
NotifyCheckedChanged();
|
|
}
|
|
else
|
|
{
|
|
SetChildChecked(this, check, isManual);
|
|
ParentNode?.UpdateCheckState();
|
|
}
|
|
StateHasChanged();
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the checkbox status of child nodes
|
|
/// </summary>
|
|
/// <param name="subnode"></param>
|
|
/// <param name="check"></param>
|
|
/// <param name="isManual"></param>
|
|
private bool SetChildChecked(TreeNode<TItem> subnode, bool check, bool isManual)
|
|
{
|
|
_actualChecked = ((!Disabled && !DisableCheckbox) || !isManual) ? check : _actualChecked;
|
|
var hasChecked = false;
|
|
var hasUnChecked = false;
|
|
if (subnode.HasChildNodes)
|
|
{
|
|
foreach (var child in subnode.ChildNodes)
|
|
{
|
|
if (!child.Checkable) continue;
|
|
if (child.SetChildChecked(child, check, isManual))
|
|
hasChecked = true;
|
|
else
|
|
hasUnChecked = true;
|
|
}
|
|
if (hasChecked || hasUnChecked)
|
|
_actualChecked = !hasUnChecked;
|
|
}
|
|
Indeterminate = hasChecked && hasUnChecked;
|
|
if (Indeterminate)
|
|
_actualChecked = false;
|
|
NotifyCheckedChanged();
|
|
return _actualChecked;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update check status
|
|
/// </summary>
|
|
/// <param name="halfChecked"></param>
|
|
private void UpdateCheckState(bool? halfChecked = null)
|
|
{
|
|
if (!Checkable) return;
|
|
if (halfChecked == true)
|
|
{
|
|
//If the child node is indeterminate, the parent node must is indeterminate.
|
|
_actualChecked = false;
|
|
Indeterminate = true;
|
|
}
|
|
else if (HasChildNodes == true)
|
|
{
|
|
//Determines the selection status of the current node
|
|
bool hasChecked = false;
|
|
bool hasUnchecked = false;
|
|
|
|
foreach (var item in ChildNodes)
|
|
{
|
|
if (!item.Checkable) continue;
|
|
if (item.Indeterminate)
|
|
{
|
|
hasChecked = true;
|
|
hasUnchecked = true;
|
|
break;
|
|
}
|
|
else if (item._actualChecked)
|
|
{
|
|
hasChecked = true;
|
|
}
|
|
else if (!item._actualChecked)
|
|
{
|
|
hasUnchecked = true;
|
|
}
|
|
}
|
|
|
|
if (hasChecked && !hasUnchecked)
|
|
{
|
|
_actualChecked = true;
|
|
Indeterminate = false;
|
|
}
|
|
else if (!hasChecked && hasUnchecked)
|
|
{
|
|
_actualChecked = false;
|
|
Indeterminate = false;
|
|
}
|
|
else if (hasChecked && hasUnchecked)
|
|
{
|
|
_actualChecked = false;
|
|
Indeterminate = true;
|
|
}
|
|
}
|
|
NotifyCheckedChanged();
|
|
|
|
if (ParentNode != null)
|
|
ParentNode.UpdateCheckState(Indeterminate);
|
|
|
|
if (ParentNode == null)
|
|
StateHasChanged();
|
|
}
|
|
|
|
private void NotifyCheckedChanged()
|
|
{
|
|
if (CheckedChanged.HasDelegate)
|
|
CheckedChanged.InvokeAsync(_actualChecked);
|
|
}
|
|
|
|
#endregion Checkbox
|
|
|
|
#region Title
|
|
|
|
[Parameter]
|
|
public bool Draggable { get; set; }
|
|
|
|
private string _icon;
|
|
|
|
/// <summary>
|
|
/// The icon in front of the node
|
|
/// </summary>
|
|
[Parameter]
|
|
public string Icon
|
|
{
|
|
get
|
|
{
|
|
if (TreeComponent.IconExpression != null)
|
|
return TreeComponent.IconExpression(this);
|
|
else
|
|
return _icon;
|
|
}
|
|
set
|
|
{
|
|
_icon = value;
|
|
}
|
|
}
|
|
|
|
[Parameter]
|
|
public RenderFragment<TreeNode<TItem>> IconTemplate { get; set; }
|
|
|
|
[Parameter]
|
|
public string SwitcherIcon { get; set; }
|
|
|
|
[Parameter]
|
|
public RenderFragment<TreeNode<TItem>> SwitcherIconTemplate { get; set; }
|
|
|
|
private string _title;
|
|
|
|
[Parameter]
|
|
public string Title
|
|
{
|
|
get
|
|
{
|
|
if (TreeComponent.TitleExpression != null)
|
|
return TreeComponent.TitleExpression(this);
|
|
else
|
|
return _title;
|
|
}
|
|
set
|
|
{
|
|
_title = value;
|
|
}
|
|
}
|
|
|
|
[Parameter]
|
|
public RenderFragment TitleTemplate { get; set; }
|
|
|
|
/// <summary>
|
|
/// title是否包含SearchValue(搜索使用)
|
|
/// </summary>
|
|
public bool Matched { get; set; }
|
|
|
|
public bool Hidden { get; set; }
|
|
|
|
/// <summary>
|
|
/// 子节点存在满足搜索条件,所以夫节点也需要显示
|
|
/// </summary>
|
|
internal bool HasChildMatched { get; set; }
|
|
|
|
#endregion Title
|
|
|
|
#region data binding
|
|
|
|
[Parameter]
|
|
public TItem DataItem { get; set; }
|
|
|
|
private IList<TItem> ChildDataItems
|
|
{
|
|
get
|
|
{
|
|
if (TreeComponent.ChildrenExpression != null)
|
|
{
|
|
var childItems = TreeComponent.ChildrenExpression(this);
|
|
if (childItems is IList<TItem> list)
|
|
{
|
|
return list;
|
|
}
|
|
return childItems?.ToList() ?? new List<TItem>();
|
|
}
|
|
else
|
|
return new List<TItem>();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获得上级数据集合
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public IList<TItem> GetParentChildDataItems()
|
|
{
|
|
if (ParentNode != null)
|
|
return ParentNode.ChildDataItems;
|
|
else
|
|
return TreeComponent.DataSource as IList<TItem> ?? TreeComponent.DataSource.ToList();
|
|
}
|
|
|
|
#endregion data binding
|
|
|
|
#region Node data operation
|
|
|
|
/// <summary>
|
|
/// Add child node
|
|
/// </summary>
|
|
/// <param name="dataItem"></param>
|
|
public void AddChildNode(TItem dataItem)
|
|
{
|
|
ChildDataItems.Add(dataItem);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a node next the node
|
|
/// </summary>
|
|
/// <param name="dataItem"></param>
|
|
public void AddNextNode(TItem dataItem)
|
|
{
|
|
var parentChildDataItems = GetParentChildDataItems();
|
|
var index = parentChildDataItems.IndexOf(DataItem);
|
|
parentChildDataItems.Insert(index + 1, dataItem);
|
|
|
|
AddNodeAndSelect(dataItem);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a node before the node
|
|
/// </summary>
|
|
/// <param name="dataItem"></param>
|
|
public void AddPreviousNode(TItem dataItem)
|
|
{
|
|
var parentChildDataItems = GetParentChildDataItems();
|
|
var index = parentChildDataItems.IndexOf(DataItem);
|
|
parentChildDataItems.Insert(index, dataItem);
|
|
|
|
AddNodeAndSelect(dataItem);
|
|
}
|
|
|
|
/// <summary>
|
|
/// remove
|
|
/// </summary>
|
|
public void Remove()
|
|
{
|
|
var parentChildDataItems = GetParentChildDataItems();
|
|
parentChildDataItems.Remove(DataItem);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The node moves into the child node
|
|
/// </summary>
|
|
/// <param name="treeNode">target node</param>
|
|
public void MoveInto(TreeNode<TItem> treeNode)
|
|
{
|
|
if (treeNode == this || DataItem.Equals(treeNode.DataItem)) return;
|
|
var parentChildDataItems = GetParentChildDataItems();
|
|
parentChildDataItems.Remove(DataItem);
|
|
treeNode.AddChildNode(DataItem);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Move up the nodes
|
|
/// </summary>
|
|
public void MoveUp()
|
|
{
|
|
var parentChildDataItems = GetParentChildDataItems();
|
|
var index = parentChildDataItems.IndexOf(DataItem);
|
|
if (index == 0) return;
|
|
parentChildDataItems.RemoveAt(index);
|
|
parentChildDataItems.Insert(index - 1, DataItem);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Move down the node
|
|
/// </summary>
|
|
public void MoveDown()
|
|
{
|
|
var parentChildDataItems = GetParentChildDataItems();
|
|
var index = parentChildDataItems.IndexOf(DataItem);
|
|
if (index == parentChildDataItems.Count - 1) return;
|
|
parentChildDataItems.RemoveAt(index);
|
|
parentChildDataItems.Insert(index + 1, DataItem);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public void Downgrade()
|
|
{
|
|
var previousNode = GetPreviousNode();
|
|
if (previousNode == null) return;
|
|
var parentChildDataItems = GetParentChildDataItems();
|
|
parentChildDataItems.Remove(DataItem);
|
|
previousNode.AddChildNode(DataItem);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Upgrade nodes
|
|
/// </summary>
|
|
public void Upgrade()
|
|
{
|
|
if (ParentNode == null) return;
|
|
var parentChildDataItems = ParentNode.GetParentChildDataItems();
|
|
var index = parentChildDataItems.IndexOf(ParentNode.DataItem);
|
|
Remove();
|
|
parentChildDataItems.Insert(index + 1, DataItem);
|
|
}
|
|
|
|
private void AddNodeAndSelect(TItem dataItem)
|
|
{
|
|
var tn = ChildNodes.FirstOrDefault(treeNode => treeNode.DataItem.Equals(dataItem));
|
|
if (tn != null)
|
|
{
|
|
_ = Expand(true);
|
|
tn.SetSelected(true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Drag and drop into child nodes
|
|
/// </summary>
|
|
/// <param name="treeNode">目标</param>
|
|
internal void DragMoveInto(TreeNode<TItem> treeNode)
|
|
{
|
|
if (TreeComponent.DataSource == null || !TreeComponent.DataSource.Any())
|
|
return;
|
|
if (treeNode == this || DataItem.Equals(treeNode.DataItem)) return;
|
|
|
|
Remove();
|
|
|
|
treeNode.AddChildNode(DataItem);
|
|
treeNode.IsLeaf = false;
|
|
_ = treeNode.Expand(true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Drag and drop to the bottom of the target
|
|
/// </summary>
|
|
/// <param name="treeNode">目标</param>
|
|
internal void DragMoveDown(TreeNode<TItem> treeNode)
|
|
{
|
|
if (TreeComponent.DataSource == null || !TreeComponent.DataSource.Any())
|
|
return;
|
|
if (treeNode == this || DataItem.Equals(treeNode.DataItem)) return;
|
|
Remove();
|
|
treeNode.AddNextNode(DataItem);
|
|
}
|
|
|
|
#endregion Node data operation
|
|
|
|
public override async Task SetParametersAsync(ParameterView parameters)
|
|
{
|
|
var isExpandedChanged = parameters.IsParameterChanged(nameof(Expanded), _expanded);
|
|
var isCheckedChanged = parameters.IsParameterChanged(nameof(Checked), _checked);
|
|
var isSelectedChanged = parameters.IsParameterChanged(nameof(Selected), _selected);
|
|
await base.SetParametersAsync(parameters);
|
|
if (isExpandedChanged)
|
|
{
|
|
await Expand(_expanded);
|
|
}
|
|
if (isCheckedChanged)
|
|
{
|
|
SetChecked(_checked);
|
|
}
|
|
if (isSelectedChanged)
|
|
{
|
|
SetSelected(_selected);
|
|
}
|
|
}
|
|
|
|
protected override void OnInitialized()
|
|
{
|
|
SetTreeNodeClassMapper();
|
|
if (ParentNode != null)
|
|
ParentNode.AddNode(this);
|
|
else
|
|
{
|
|
TreeComponent.AddChildNode(this);
|
|
}
|
|
|
|
TreeComponent.AddNode(this);
|
|
|
|
// Expand
|
|
var isExpanded = false;
|
|
if (_expanded)
|
|
{
|
|
isExpanded = true;
|
|
}
|
|
else
|
|
{
|
|
var expandedKeys = TreeComponent.CachedExpandedKeys ?? TreeComponent.DefaultExpandedKeys;
|
|
if (expandedKeys != null)
|
|
{
|
|
isExpanded = expandedKeys != null && expandedKeys.Contains(Key);
|
|
}
|
|
else
|
|
{
|
|
isExpanded = TreeComponent.DefaultExpandAll || (ParentNode == null && TreeComponent.DefaultExpandParent);
|
|
}
|
|
}
|
|
_ = DoExpand(isExpanded);
|
|
|
|
if (TreeComponent.DisabledExpression != null)
|
|
Disabled = TreeComponent.DisabledExpression(this);
|
|
|
|
if (TreeComponent.CheckableExpression != null)
|
|
Checkable = TreeComponent.CheckableExpression(this);
|
|
|
|
if (Checkable)
|
|
{
|
|
var isChecked = false;
|
|
if (_checked)
|
|
{
|
|
isChecked = true;
|
|
}
|
|
else
|
|
{
|
|
var checkedKeys = TreeComponent.CachedCheckedKeys ?? TreeComponent.DefaultCheckedKeys;
|
|
if (checkedKeys != null)
|
|
isChecked = checkedKeys.Any(k => k == Key);
|
|
}
|
|
DoCheck(isChecked, false, false);
|
|
}
|
|
|
|
if (TreeComponent.Selectable)
|
|
{
|
|
var isSelected = false;
|
|
if (_selected)
|
|
{
|
|
isSelected = true;
|
|
}
|
|
else
|
|
{
|
|
var selectedKeys = TreeComponent.CachedSelectedKeys ?? TreeComponent.DefaultSelectedKeys;
|
|
if (selectedKeys != null)
|
|
isSelected = selectedKeys.Any(k => k == Key);
|
|
}
|
|
DoSelect(isSelected, TreeComponent.Multiple, false);
|
|
}
|
|
base.OnInitialized();
|
|
}
|
|
}
|
|
}
|