父节点可以右击新增子节点,新增数量可选;
子节点可以右击删除自身,可以在上方/下方插入同层级节点,可以上移或下移;
排序的部分,需要同时修改数据源列表的顺序。
目录树为三层级,根节点、菜单节点、详情节点;
根节点无右击事件
菜单节点右击事件为新增子节点
详情节点右击为插入、排序和删除。
//1、数据源结构定义
public class DataSource : DataSourceItem
{
public List<DataSourceItem> Children { get; set; }
}
public class DataSourceItem
{
public Guid UniqueId { get; set; }
public string Name { get; set; }
}
//一些假数据
DataSource sys = new DataSource
{
UniqueId = Guid.NewGuid()
};
DataSource Node1 = new DataSource
{
UniqueId = Guid.NewGuid(),
Name = "Node1",
Children = new List<DataSourceItem>() {
new DataSourceItem{ UniqueId = Guid.NewGuid(),Name = "node" },
new DataSourceItem{ UniqueId = Guid.NewGuid(),Name = "node" },
}
};
//2、通用节点结构定义
public class Node
{
public Guid UniqueId { get; set; }
public string Name { get; set; }
public Guid ParentId { get; set; }
public Level Level { get; set; }
}
public enum Level
{
root,
menu,
detail
}
//3、数据源转通用节点字典
private Dictionary<DataSource, Node> nodeList = new Dictionary<DataSource, Node>();
//根节点
nodeList.Add(sys, new Node {
UniqueId = sys.UniqueId,
Name = "Sys",
ParentId = sys.UniqueId,
Level = Level.root
});
internal Node GetRoot()
{
return nodeList[sys];
}
//菜单节点
var node1 = GetNode(Node1, sys.UniqueId);
nodeList.Add(node1.Key, node1.Value);
internal Node GetMenuNodeList()
{
return from a in nodeList
where a.Value.ParentId == sys.UniqueId
&& a.Value.UniqueId != sys.UniqueId
select a.Value;
}
private KeyValuePair<T, Node> GetNode<T>(T o, Guid parentid, Level level = Level.menu)
{
return new KeyValuePair<T, Node>(o, new Node {
UniqueId = (Guid)o.GetType().GetProperty("UniqueId").GetValue(o),
Name = o.GetType().GetProperty("Name").GetValue(o).ToString(),
ParentId = parentid,
Level = level });
}
//详情节点
private List<Node> GetDetailNodeList(DataSource parent, Guid parentid)
{
List<Node> res = new List<Node>();
var list = parent.Children;
foreach (var o in list)
{
var node = GetNode(o, parentid, Level.detail);
res.Add(GetNode(o, parentid, Level.detail).Value);
}
return res;
}
//4、VM获取子节点(菜单层和详情层)
internal IEnumerable<Node> GetChildren(Guid parentId)
{
if (parentId == sys.UniqueId) return GetMenuNodeList();
foreach (var a in nodeList)
{
if (a.Value.UniqueId == parentId)
return GetDetailNodeList(a.Key, parentId);
}
return new List<Node>();
}
//5、增加节点
internal void AddNode(Node i, Guid parentId)
{
foreach (var a in nodeList)
{
if (a.Value.UniqueId == parentId)
{
a.Key.Children.Add(
new DataSourceItem() { UniqueId = i.UniqueId, Name = i.Name });
}
}
}
//6、删除节点
internal void RemoveNode(Guid nodeId, Guid parentId)
{
foreach (var a in nodeList)
{
if (a.Value.UniqueId == parentId)
{
var t = a.Key.Children.Find(_ => _.UniqueId == nodeId);
if (t != null) a.Key.Children.Remove(t);
}
}
}
//7、插入节点
internal void InsertNode(Node i, Guid tUid, Guid parentId, bool isBefore)
{
foreach (var a in nodeList)
{
if (a.Value.UniqueId == parentId)
{
var t = a.Key.Children.FindIndex(_ => _.UniqueId == tUid);
if (t != -1)
a.Key.Children.Insert(isBefore ? t : (t + 1),
new DataSourceItem { UniqueId = i.UniqueId, Name = i.Name });
}
}
}
//8、交换节点顺序
internal void SwapNode(Guid tUid, Guid parentId, bool isBefore)
{
foreach (var a in nodeList)
{
if (a.Value.UniqueId == parentId)
{
var t = a.Key.Children.FindIndex(_ => _.UniqueId == tUid);
if (t != -1)
{
if (isBefore && t == 0) return;
if (!isBefore && t == (a.Key.Children.Count - 1)) return;
int swapId = isBefore ? (t - 1) : (t + 1);
var temp = a.Key.Children[swapId];
a.Key.Children[swapId] = a.Key.Children[t];
a.Key.Children[t] = temp;
}
}
}
}
其中涉及
利用ItemTemplateSelector选择器实现目录树不同层级不同UI的方法
public class ContextMenuDataTemplateSelector : DataTemplateSelector
{
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
DataTemplate template = null;
if (item is MenuOrgNodeViewModel)
{
template = element.FindResource("menuNodeTemplate") as HierarchicalDataTemplate;
}
else if (item is RootOrgNodeViewModel)
{
template = element.FindResource("rootNodeTemplate") as HierarchicalDataTemplate;
}
else if (item is DetailOrgNodeViewModel)
{
template = element.FindResource("detailNodeTemplate") as DataTemplate;
}
return template;
}
}
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
public class NodeViewModel : ObservableObject
{
private Guid uniqueId;
private string name;
private Level level;
private bool isSelected;
public Guid UniqueId { get => uniqueId; set => uniqueId = value; }
public string Name { get => name; set => name = value; }
public Level Level { get => level; set => level = value; }
public bool IsSelected { get => isSelected; set => OnPropertyChanged(ref isSelected, value); }
internal NodeViewModel(Node node)
{
this.UniqueId = node.UniqueId;
this.Name = node.Name;
this.level = node.Level;
}
}
每次增删排序后应当重新获取子节点,才能更新到页面上
public class MenuOrgNodeViewModel : OrgNodeViewModel
{
internal MenuOrgNodeViewModel(Node i, OrgNodeViewModel parent = null) : base(i, parent)
{
}
}
public class RootOrgNodeViewModel : OrgNodeViewModel
{
internal RootOrgNodeViewModel(Node i, OrgNodeViewModel parent = null) : base(i, parent)
{
}
}
public class DetailOrgNodeViewModel : OrgNodeViewModel
{
internal DetailOrgNodeViewModel(Node i, OrgNodeViewModel parent = null) : base(i, parent)
{
}
}
public class OrgNodeViewModel : NodeViewModel
{
internal OrgNodeViewModel(Node i, OrgNodeViewModel parent = null)
: base(i)
{
this.parent = parent;
}
private ObservableCollection<OrgNodeViewModel> children;
private OrgNodeViewModel parent;
public ObservableCollection<OrgNodeViewModel> Children
{
get
{
if (children == null)
return GetChildren();
return children;
}
set
{
OnPropertyChanged(ref children, value);
}
}
public int Index {
get {
return parent.Children.ToList()
.FindIndex(_ => _.UniqueId == this.UniqueId);
}
}
private ObservableCollection<OrgNodeViewModel> GetChildren()
{
children = new ObservableCollection<OrgNodeViewModel>();
foreach (Node i in TreeDataAccess.GetIns()
.GetChildren(this.UniqueId))
{
if (i.Level == Level.menu)
children.Add(new MenuOrgNodeViewModel(i, this));
else if (i.Level == Level.detail)
children.Add(new DetailOrgNodeViewModel(i, this));
}
return children;
}
private void RefreshChildren()
{
Children = GetChildren();
}
public void AddChild(int count = 1)
{
for (int i = 0; i < count; i++)
{
TreeDataAccess.GetIns().AddNode(new Node {
UniqueId = Guid.NewGuid(),
Name = "add" + i,
Level = Level.detail ,
ParentId = this.UniqueId}, this.UniqueId);
}
RefreshChildren();
}
public void DeleteSelf() {
TreeDataAccess.GetIns()
.RemoveNode(this.UniqueId,this.parent.UniqueId);
this.parent.RefreshChildren();
}
public void InsertNode(bool isBefore) {
TreeDataAccess.GetIns().InsertNode(new Node {
UniqueId = Guid.NewGuid(),
Name = "INSERT",
Level = Level.detail,
ParentId = this.parent.UniqueId },
this.UniqueId,
this.parent.UniqueId,
isBefore);
this.parent.RefreshChildren();
}
public void SwapNode(bool isBefore)
{
TreeDataAccess.GetIns().SwapNode(
this.UniqueId,
this.parent.UniqueId,
isBefore);
this.parent.RefreshChildren();
}
}
public class TreeViewWithEditViewModel : ObservableObject
{
private List<OrgNodeViewModel> root;
public List<OrgNodeViewModel> Root
{
get
{
if (root == null)
{
root = new List<OrgNodeViewModel>
{
new RootOrgNodeViewModel(TreeDataAccess.GetIns().GetRoot())
};
}
return root;
}
set => OnPropertyChanged(ref root, value);
}
private int addCount = 1;
public int AddCount
{
get { return addCount; }
set { OnPropertyChanged(ref addCount, value); }
}
public TreeViewWithEditViewModel()
{
AddNodeCommand = new RelayCommand((o) =>
{
(o as OrgNodeViewModel).AddChild(AddCount);
AddCount = 1;
});
DeleteNodeCommand = new RelayCommand((o) =>
{
(o as OrgNodeViewModel).DeleteSelf();
});
InsertBeforeNodeCommand = new RelayCommand((o) =>
{
(o as OrgNodeViewModel).InsertNode(true);
});
InsertAfterNodeCommand = new RelayCommand((o) =>
{
(o as OrgNodeViewModel).InsertNode(false);
});
SwapBeforeNodeCommand = new RelayCommand((o) =>
{
(o as OrgNodeViewModel).SwapNode(true);
});
SwapAfterNodeCommand = new RelayCommand((o) =>
{
(o as OrgNodeViewModel).SwapNode(false);
});
}
public RelayCommand AddNodeCommand { get; }
public RelayCommand DeleteNodeCommand { get; }
public RelayCommand InsertBeforeNodeCommand { get; }
public RelayCommand InsertAfterNodeCommand { get; }
public RelayCommand SwapBeforeNodeCommand { get; }
public RelayCommand SwapAfterNodeCommand { get; }
public RelayCommand SwapNodeCommand { get; }
}