Recursive adding XML into TreeView

后端 未结 3 1703
天涯浪人
天涯浪人 2021-01-25 06:56

I\'m trying to import an XML file of nodes into the same node structure in a TreeView using C#. I have found a lot of example that use a single node structure, but have had a lo

3条回答
  •  执笔经年
    2021-01-25 07:54

    The reason this is complicated is that your tree node hierarchy does not correspond 1-1 to your XML hierarchy. In situations like this, I suggest introducing intermediate classes to provide a view into the base XML model data. In WPF these classes would be the View Model, but the windows forms TreeView doesn't support data binding. Despite this, the abstraction of a view model is useful here.

    First, some basic view model interfaces and classes to tie together TreeNode and XElement hierarchies:

    public interface ITreeNodeViewModel
    {
        string Name { get; }
    
        string Text { get; }
    
        object Tag { get; }
    
        object Model { get; }
    
        IEnumerable Children { get; }
    }
    
    public abstract class TreeNodeViewModel : ITreeNodeViewModel
    {
        readonly T model;
    
        public TreeNodeViewModel(T model)
        {
            this.model = model;
        }
    
        public T Model { get { return model; } }
    
        #region ITreeNodeProxy Members
    
        public abstract string Name { get; }
    
        public abstract string Text { get; }
    
        public virtual object Tag { get { return this; } } 
    
        public abstract IEnumerable Children { get; }
    
        #endregion
    
        #region ITreeNodeViewModel Members
    
        object ITreeNodeViewModel.Model
        {
            get { return Model; }
        }
    
        #endregion
    }
    
    public abstract class XElementTreeNodeViewModel : TreeNodeViewModel
    {
        public XElementTreeNodeViewModel(XElement node) : base(node) {
            if (node == null)
                throw new ArgumentNullException();
        }
    
        public XNamespace Namespace { get { return Model.Name.Namespace; } }
    
        public override string Name
        {
            get { return Model.Name.ToString();  }
        }
    }
    

    Next, a couple extension classes:

    public static class TreeViewExtensions
    {
        public static void PopulateNodes(this TreeView treeView, IEnumerable viewNodes)
        {
            treeView.BeginUpdate();
            try
            {
                treeView.Nodes.PopulateNodes(viewNodes);
            }
            finally
            {
                treeView.EndUpdate();
            }
        }
    
        public static void PopulateNodes(this TreeNodeCollection nodes, IEnumerable viewNodes)
        {
            nodes.Clear();
            if (viewNodes == null)
                return;
            foreach (var viewNode in viewNodes)
            {
                var name = viewNode.Name;
                var text = viewNode.Text;
                if (string.IsNullOrEmpty(text))
                    text = name;
                var node = new TreeNode { Name = name, Text = text, Tag = viewNode.Tag };
                nodes.Add(node);
                PopulateNodes(node.Nodes, viewNode.Children);
                node.Expand();
            }
        }
    }
    
    public static class XObjectExtensions
    {
        public static string TextValue(this XContainer node)
        {
            if (node == null)
                return null;
            //return string.Concat(node.Nodes().OfType().Select(tx => tx.Value));  c# 4.0
            return node.Nodes().OfType().Select(tx => tx.Value).Aggregate(new StringBuilder(), (sb, s) => sb.Append(s)).ToString();
        }
    
        public static IEnumerable Elements(this IEnumerable elements, XName name)
        {
            return elements.SelectMany(el => el.Elements(name));
        }
    }
    

    Next, view models for the three levels of your tree:

    class ContainerViewModel : XElementTreeNodeViewModel
    {
        public ContainerViewModel(XElement node) : base(node) { }
    
        public override string Text
        {
            get
            {
                return Model.Element(Namespace + "ContainerName").TextValue();
            }
        }
    
        public override IEnumerable Children
        {
            get { return Enumerable.Empty(); }
        }
    }
    
    class ClassificationViewModel : XElementTreeNodeViewModel
    {
        public ClassificationViewModel(XElement node) : base(node) { }
    
        public override string Text
        {
            get
            {
                return Model.Element(Namespace + "ClassificationName").TextValue();
            }
        }
    
        public override IEnumerable Children
        {
            get
            {
                return Model.Elements(Namespace + "Containers").Elements(Namespace + "Container").Select(xn => (ITreeNodeViewModel)new ContainerViewModel(xn));
            }
        }
    }
    
    class GroupViewModel : XElementTreeNodeViewModel
    {
        public GroupViewModel(XElement node) : base(node) { }
    
        public override string Text
        {
            get
            {
                return Model.Element(Namespace + "GroupName").TextValue();
            }
        }
    
        public override IEnumerable Children
        {
            get
            {
                return Model.Elements(Namespace + "Classifications").Elements(Namespace + "Classification").Select(xn => (ITreeNodeViewModel)new ClassificationViewModel(xn));
            }
        }
    }
    

    Now, building your tree becomes quite simple:

            var xdoc = XDocument.Load("ProductDocument.xml");
            var ns = xdoc.Root.Name.Namespace;
            treeView1.PopulateNodes(xdoc.Root.Elements(ns + "ProductGroup").Elements(ns + "Group").Select(xn => (ITreeNodeViewModel)new GroupViewModel(xn)));
    

    And the result:

    enter image description here

    Later, if you wish to add editing functionality to your tree, you can add the appropriate methods to ITreeNodeViewModel -- for instance, a setter method for Text. Since the ITreeNodeViewModel has saved itself in the TreeNode.Tag the appropriate methods will be available.

提交回复
热议问题