Recursive adding XML into TreeView

后端 未结 3 1702
天涯浪人
天涯浪人 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:51

    You are using the wrong variable in your loop over the Classification Elements. Replace groupElement with ClassificationElement inside the loop.

    Change:

    foreach (XElement ClassificationElement in groupElement.Descendants("Classification"))
    {
        // groupElement.Element("ClassificationName") is null:
        treProducts.SelectedNode.Nodes.Add(groupElement.Element("ClassificationName").Value);
        ...
    }
    

    to

    foreach (XElement ClassificationElement in groupElement.Descendants("Classification"))
    {
    treProducts.SelectedNode.Nodes.Add(ClassificationElement.Element("ClassificationName").Value);
        ...
    }
    
    0 讨论(0)
  • 2021-01-25 07:52

    I suggest recursion

    void AddNodes(XElement parentElement, TreeNode parent = null)
    {
        Queue<XElement> queue = new Queue<XElement>(parentElement.Elements());
        while (queue.Count > 0)
        {
            TreeNode child = parent;
            XElement element = queue.Dequeue();
            if (!element.HasElements)
            {
                string value = element.Value;
                element = (XElement)element.NextNode;
                if (null != element && !element.HasElements)
                    value = element.Value;
    
                if (null == parent)
                    treeView1.Nodes.Add(child = new TreeNode(value));
                else
                    parent.Nodes.Add(child = new TreeNode(value));
                child.Expand();
                element = queue.Dequeue();
            }
            AddNodes(element, child);
        }
    }
    
    AddNodes(XElement.Load("ProductDocument.xml"));
    

    enter image description here

    Note: dbc's answer is probably better if your XML structure is likely to change, but if you are given it as it currently stands, and it won't change - then this will slurp it right into the tree quickly, without a lot of overhead.

    0 讨论(0)
  • 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<ITreeNodeViewModel> Children { get; }
    }
    
    public abstract class TreeNodeViewModel<T> : 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<ITreeNodeViewModel> Children { get; }
    
        #endregion
    
        #region ITreeNodeViewModel Members
    
        object ITreeNodeViewModel.Model
        {
            get { return Model; }
        }
    
        #endregion
    }
    
    public abstract class XElementTreeNodeViewModel : TreeNodeViewModel<XElement>
    {
        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<ITreeNodeViewModel> viewNodes)
        {
            treeView.BeginUpdate();
            try
            {
                treeView.Nodes.PopulateNodes(viewNodes);
            }
            finally
            {
                treeView.EndUpdate();
            }
        }
    
        public static void PopulateNodes(this TreeNodeCollection nodes, IEnumerable<ITreeNodeViewModel> 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<XText>().Select(tx => tx.Value));  c# 4.0
            return node.Nodes().OfType<XText>().Select(tx => tx.Value).Aggregate(new StringBuilder(), (sb, s) => sb.Append(s)).ToString();
        }
    
        public static IEnumerable<XElement> Elements(this IEnumerable<XElement> 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<ITreeNodeViewModel> Children
        {
            get { return Enumerable.Empty<ITreeNodeViewModel>(); }
        }
    }
    
    class ClassificationViewModel : XElementTreeNodeViewModel
    {
        public ClassificationViewModel(XElement node) : base(node) { }
    
        public override string Text
        {
            get
            {
                return Model.Element(Namespace + "ClassificationName").TextValue();
            }
        }
    
        public override IEnumerable<ITreeNodeViewModel> Children
        {
            get
            {
                return Model.Elements(Namespace + "Containers").Elements<XElement>(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<ITreeNodeViewModel> Children
        {
            get
            {
                return Model.Elements(Namespace + "Classifications").Elements<XElement>(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.

    0 讨论(0)
提交回复
热议问题