【WPF】自定义控件:TreeViewEditControl-可同层级增删排序的目录树

可以在同一层级内增删排序节点的目录树实现,效果如下:
【WPF】自定义控件:TreeViewEditControl-可同层级增删排序的目录树_第1张图片

父节点可以右击新增子节点,新增数量可选;
子节点可以右击删除自身,可以在上方/下方插入同层级节点,可以上移或下移;
排序的部分,需要同时修改数据源列表的顺序。

数据

目录树为三层级,根节点、菜单节点、详情节点;
根节点无右击事件
菜单节点右击事件为新增子节点
详情节点右击为插入、排序和删除。

//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;
            }
        }
    }
}

页面

View

其中涉及
控件绑定点击事件的实现方式
利用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));
}

ViewModel

每个节点对应的VM,处理基本数据
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();
    }

}
完整目录树的VM
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; }
}

你可能感兴趣的:(WPF,wpf)