递归地将XML添加到TreeView中 [英] Recursive adding XML into TreeView

查看:53
本文介绍了递归地将XML添加到TreeView中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用C#将节点的XML文件导入TreeView中的相同节点结构中.我发现了很多使用单节点结构的示例,但是在遍历XML文件并用它填充TreeView时遇到了很多问题.这是XML文件的示例:

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 lot of issues traversing the XML file and populating the TreeView with it. This is a sample of the XML file:

<?xml version="1.0"?>
<xmlRoot>
<ProductGroup>
    <Group> 
      <GroupName>Soda</GroupName>
        <Classifications>
            <Classification>
                <ClassificationName>Regular</ClassificationName>
                    <Containers>
                        <Container>
                            <ContainerType>Can</ContainerType>
                            <ContainerName>SmallCan</ContainerName>
                        </Container>
                        <Container>
                            <ContainerType>bottle</ContainerType>
                            <ContainerName>SmallBottle</ContainerName>
                        </Container>
                    </Containers>
            </Classification>
            <Classification>
                <ClassificationName>Diet</ClassificationName>
                    <Containers>
                        <Container>
                            <ContainerType>Can</ContainerType>
                            <ContainerName>SmallCan</ContainerName>
                        </Container>
                    </Containers>
            </Classification>
        </Classifications>
    </Group>
    <Group> 
      <GroupName>Water</GroupName>
        <Classifications>
            <Classification>
                <ClassificationName>Regular</ClassificationName>
                    <Containers>
                        <Container>
                            <ContainerType>Bottle</ContainerType>
                            <ContainerName>EcoBottle</ContainerName>
                        </Container>
                    </Containers>
            </Classification>
        </Classifications>
    </Group>
</ProductGroup>
</xmlRoot>

我尝试使用类似这样的东西:

I've tried using something like this:

treProducts.Nodes.Clear();
XDocument xdoc = XDocument.Load("ProductDocument.xml");
foreach (XElement groupElement in xdoc.Descendants("Group"))
{
    treProducts.Nodes.Add(groupElement.Element("GroupName").Value);
    treProducts.SelectedNode = treProducts.Nodes[groupElement.Element("GroupName").Value];
    foreach (XElement ClassificationElement in groupElement.Descendants("Classification"))
    {
        treProducts.SelectedNode.Nodes.Add(groupElement.Element("ClassificationName").Value);
        treProducts.SelectedNode = treProducts.Nodes[groupElement.Element("ClassificationName").Value];
        foreach (XElement ContainerElement in groupElement.Descendants("Container"))
        {
            treProducts.SelectedNode.Nodes.Add(ContainerElement.Element("ContainerName").Value);
        }
    }
}

我正试图让树显示出来:

I'm trying to get the tree to show:

Soda
    Regular
        SmallCan
        SmallBottle
    Diet
        SmallCan
Water
    Regular
        EcoBottle

...但是树只显示了Soda,并且似乎跳过了其余部分,除非我注释掉嵌套的foreach语句,否则它将显示Soda和Water. 我使用的语法有问题,我想知道是否有人更了解Linq可以帮助您查看代码哪里有问题.

...but the tree is only showing Soda and it seems to skip the rest, except if I comment out the nested foreach statements, it will show Soda and Water. There's something wrong with the syntax I'm using and I'm wondering if someone who understands Linq better can help see where the code is wrong.

推荐答案

之所以复杂,是因为您的树节点层次结构与XML层次结构不对应1-1.在这种情况下,我建议引入中间类以提供对基本XML模型数据的视图.在WPF中,这些类将是视图模型",但是Windows窗体不支持数据绑定.尽管如此,这里的视图模型抽象还是很有用的.

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.

首先,一些基本的视图模型接口和类将TreeNodeXElement层次结构联系在一起:

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)));

结果:

稍后,如果您希望向树添加编辑功能,则可以向ITreeNodeViewModel添加适当的方法-例如,Text的setter方法.由于ITreeNodeViewModel已将自身保存在

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.

这篇关于递归地将XML添加到TreeView中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆