0

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

下面是我想在 wpf treeview 中显示的 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>

PS 下面是我不成功的尝试。我正在尝试将 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;
                }            
        }
4

1 回答 1

3

为了在 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<T> 允许在 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 数据,给定的示例代码可能会表现得很有趣。

(此示例代码假定 <dir>,<file> 元素是 <Folder> 节点的子元素。但是,在您的 XML 中,<dir>,<file> 是 <FolderName> 元素的子元素,这看起来像是一个错误. 如果这确实是故意的,您将需要相应地采用示例代码。)

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

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>

请注意模板的DataTypeItemsSource参数,该参数使用子条目的集合绑定到 DirectoryEntry 的ChildEntries属性。

对于文件条目,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 。)

于 2013-11-15T02:12:08.810 回答