【问题标题】:How to display folder structure from xml file to wpf treeview?如何显示从 xml 文件到 wpf treeview 的文件夹结构?
【发布时间】:2013-11-28 04:09:31
【问题描述】:

如何显示从 xml 文件到 wpf 树视图的文件夹结构?我已经尝试了 Josh Smith 的文章 http://www.codeproject.com/Articles/26288/Simplifying-the-WPF-TreeView-by-Using-the-ViewModel 的第二部分(区域),但是,如何扫描或显示包含未知级别数的文件夹结构的 xml 文件?

下面是我想在 wpf 树视图中显示的 xml 文件。将名称作为属性而不是元素是更好的做法吗?

<?xml version="1.0" encoding="utf-8"?>
<Folder>
  <Folders />
  <FolderName>
    <dir>
  <name>RootDir</name> 
  <file>
    <name>dobatchfile.bat</name>
  </file>

  <dir>
    <name>Build</name>
    <dir>
      <name>BuildProcessTemplates</name>
      <file>
        <name>AzureContinuousDeployment.11.xaml</name>
      </file>
      <file>
        <name>DefaultTemplate.11.1.xaml</name>
      </file>

    </dir>

  </dir>

</dir>
  </FolderName>
</Folder>

附: 以下是我不成功的尝试。我正在尝试将 xml 文件内容保存到列表中,以使用 mvvm 模式绑定到我的 wpf 树视图。

public static List<Directory> GetRegions()
        {
            List<Directory> ret = new List<Directory>();
            //var expandolist = GetExpandoFromXml("C:\\New folder/Region1.xml", "Regions", "");
            var expandolist = GetExpandoFromXmlRoot("c:\\temp/SerializationOverview.xml", "Regions", "");            
            expandolist.ToList().ForEach(element =>
            {
                var dictionary = element as IDictionary<string, object>;
               // dictionary.ToList().ForEach(d => ret.Add(new Directory(d.Key)));
                dictionary.Where(d => d.Key == "name" || d.Key == "dir").ToList().ForEach(d => ret.Add(new Directory(d.Value.ToString())));                
            });
            return ret;
        } 

 public static IEnumerable<dynamic> GetExpandoFromXml(string file, string descendantid, string Selection)
        {
            var expandoFromXml = new List<dynamic>();
            var doc = XDocument.Load(file);           
                //foreach (var element in doc.Root.Descendants(descendantid))
            foreach (var element in doc.Root.Descendants())
                {
                    dynamic expandoObject = new ExpandoObject();
                    var dictionary = expandoObject as IDictionary<string, object>;
                    foreach (var child in element.Elements().Where(e =>   e.Parent.Parent.Value.Contains(Selection)))
                    //foreach (var child in element.Descendants())
                    {
                        if (child.Name.Namespace == "")
                            dictionary[child.Name.ToString()] = child.Value.Trim();
                          //  dictionary[child.Name.ToString()] = child.Attributes().FirstOrDefault().Value;
                    }
                    yield return expandoObject;
                }            
        }

【问题讨论】:

  • 您是否已经将 XML 文件读入了某种数据结构(如果有,请出示代码)?或者,阅读 XML 文件是您的实际问题吗?
  • @elgonzo 读取 XML 文件是我的实际问题。
  • 好的,还有一些问题 :) 您需要 XML 文件中的信息仅用于 TreeView,还是您的软件中的其他地方也需要 XML 数据? TreeView 是否只显示 XML 中的文本,还是 XML 包含一些对 TreeViewItems 的可视化有影响的属性/元素?
  • @elgonzo 我只需要用于树视图的 xml 数据,并且 xml 文件可能包含一些对 TreeViewItems 的可视化有影响的属性。
  • 是否使用子元素而不是属性取决于您的需求。经验法则:将您的 XML 格式设计为尽可能简洁、易读和明确。这通常意味着使用诸如“姓名”、“日期”等信息的属性是更好的方法。但是,仅从软件的角度来看(a-ka Linq 查询),这两种方法之间没有显着差异......

标签: c# xml wpf linq treeview


【解决方案1】:

要在 TreeView 中显示 XML 文件中的数据,数据将组织在数据模型中,这使得通过 TreeView 对其进行操作和表示变得容易。


1.数据模型

树中的每个条目都将派生自一个通用的基类型。

public abstract class NamedTreeEntry
{
    public string DisplayName { get; set; }
}

由于所有条目都必须显示一个名称,因此还在基本类型中声明了一个适当的属性 DisplayName

对于我们的例子,必须考虑派生自该基本类型的两种具体类型:目录和文件。

public class FileEntry : NamedTreeEntry
{
    // ... and other file-specific public properties and methods
}

public class DirectoryEntry : NamedTreeEntry
{
    public ObservableCollection<NamedTreeEntry> ChildEntries
    {
        get { return _collChildren; }
    }
    private readonly ObservableCollection<NamedTreeEntry> _collChildren;

    public DirectoryEntry(IEnumerable<NamedTreeEntry> childEntries)
    {
        _collChildren = (childEntries != null) ?
            new ObservableCollection<NamedTreeEntry>(childEntries)
            : new ObservableCollection<NamedTreeEntry>();
    }

    // ... and other directory-specific public properties and methods
}

注意子列表的 ObservableCollection<T> 类型的用法。 尽管对于此处概述的示例而言并非绝对必要,但 ObservableCollection 允许在 TreeView 控件自动保持最新的同时动态添加或删除子条目。


2.将 XML 转换为数据模型

使用上面介绍的类型读取 XML 数据并将其转换为数据模型非常简单。使用Linq(配合XDocument/XElement),所需代码只有几行:

public DirectoryEntry CreateDataModelFromXml(Stream xmlFileStream)
{
    XDocument xDoc = XDocument.Load(xmlFileStream);
    return new DirectoryEntry(QueryChildEntries(xDoc.Element("Folder")))
    {
        Name = "ROOT"
    };
}

private IEnumerable<NamedTreeEntry> QueryChildEntries(XElement xElem)
{
    return
        from childElem in xElem.Elements()
        where childElem.Name == "dir" || childElem.Name == "file"
        select (childElem.Name == "file") ?
            (NamedTreeEntry) new FileEntry()
                {
                    Name = childElem.Element("name").Value
                }
            : new DirectoryEntry(QueryChildEntries(childElem))
                {
                    Name = childElem.Element("name").Value,
                };
}

为了可读性,任何类型的异常处理和完整性检查都被省略了。在您的真实代码中,这些事情必须完成。如果输入格式不正确/不正确的 XML 数据,给定的示例代码可能会表现得很有趣。

(此示例代码假定

, 元素是 节点的子元素。但是,在您的 XML 中,, 元素的子元素,看起来像一个错误。如果这确实是故意的,您将需要相应地采用示例代码。)

在源代码的适当位置,只需调用:

DirectoryEntry rootEntry;
using (FileStream stream = new FileStream(xmlFilePathString, FileMode.Open, FileAccess.Read))
{
    rootEntry = CreateDataModelFromXml(stream);
}


3.树视图

现在,您只需将包含顶级条目的集合分配给 TreeView 控件(在本例中称为 MyTreeView)。

MyTreeView.ItemsSource = rootEntry.ChildEntries;

如果您还想在树视图中显示根条目,请执行以下操作:

MyTreeView.ItemsSource = new DirectoryEntry[] { rootEntry };

在您的实际代码中,您可能会在 XAML 中使用数据绑定,而不是在代码隐藏中设置 ItemsSource 属性。

嗯,到目前为止我们所做的,TreeView 控件将只显示第一级中的条目。但它不会显示任何子条目,只是因为它不知道它们。是时候介绍HierarchicalDataTemplate了。 HierarchicalDataTemplate 不仅指定条目的外观,而且还有一个参数可以绑定到带有子条目的集合。

对于目录条目,HierarchicalDataTemplate 可能如下所示:

<HierarchicalDataTemplate DataType="{x:Type My:DirectoryEntry}" ItemsSource="{Binding ChildEntries}">
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="Dir: " />
        <TextBlock Text="{Binding Name}" />
    </StackPanel>
</HierarchicalDataTemplate>

注意模板的 DataType,以及将子条目集合绑定到 DirectoryEntry 的 ChildEntries 属性的 ItemsSource 参数。 p>

对于文件条目,DataTemplate 就足够了,因为它们没有子项。

<DataTemplate DataType="{x:Type My:FileEntry}">
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="File: " />
        <TextBlock Text="{Binding Name}" />
    </StackPanel>
</DataTemplate>

现在 TreeView 控件只需要知道这两个模板中的哪一个将用于哪个条目。 对于许多用例,TreeView 控件使这非常容易——只需将这两个模板添加到 TreeView 控件的本地资源字典中。 TreeView 将从其资源字典中选择第一个数据模板(HierarchicalDataTemplate 也是一个数据模板),其数据类型与相应条目的数据类型匹配。

总之,TreeView 的完整 XAML 如下所示:

    <TreeView Name="MyTreeView">
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type My:DirectoryEntry}" ItemsSource="{Binding ChildEntries}">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="Dir: " />
                    <TextBlock Text="{Binding Name}" />
                </StackPanel>
            </HierarchicalDataTemplate>
            <DataTemplate DataType="{x:Type My:FileEntry}">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="File: " />
                    <TextBlock Text="{Binding Name}" />
                </StackPanel>
            </DataTemplate>
        </TreeView.Resources>
    </TreeView>

(如果无法按数据类型选择模板,则可以使用ItemTemplateSelector。)

【讨论】:

  • 抱歉,不,示例代码应该是不言自明的。如果你有理解困难,那么你的问题不是 XML 或 TreeView,而是 C#/WPF。
猜你喜欢
  • 2022-09-23
  • 2017-01-02
  • 1970-01-01
  • 2016-01-26
  • 2012-05-20
  • 1970-01-01
  • 2017-06-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多