2013-11-14 3 views
0

Как отобразить структуру папок из xml-файла в wpf treeview? Я попробовал вторую часть (Регионы) статьи Джоша Смита http://www.codeproject.com/Articles/26288/Simplifying-the-WPF-TreeView-by-Using-the-ViewModel, но как сканировать или отображать xml-файл, содержащий структуру папок с неизвестным количеством уровней?Как отобразить структуру папок из xml-файла в wpf treeview?

Ниже представлен файл xml, который я хочу отобразить в wpf treeview. Является ли название атрибутом вместо элемента лучшей практикой?

<?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> 

P.S. Ниже приведена моя неудачная попытка. Я пытаюсь сохранить содержимое xml-файла в списке для привязки к моему wpf treeview с использованием шаблона mvvm.

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; 
       }    
     } 
+0

Вы уже читали XML-файл в какой-либо структуре данных (если да, то пожалуйста, покажите код)? Или, читаете ли XML-файл свою актуальную проблему? – elgonzo

+0

@elgonzo Чтение XML-файла - моя фактическая проблема. – user2330678

+0

Хорошо, еще несколько вопросов :) Вам нужна информация из файла XML только для вашего TreeView, или вам нужны данные XML также где-то еще в вашем программном обеспечении?Отображает ли TreeView только текст из XML или XML содержит некоторые атрибуты/элементы, которые влияют на визуализацию TreeViewItems? – elgonzo

ответ

3

Чтобы отобразить данные из файла XML в TreeView, данные будут организованы в модели данных, что делает его манипуляции и представление через TreeView легко.


1. Модель данных

Каждая запись в дереве будет получать от общего базового типа.

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

Поскольку все элементы должны отображаться имя, соответствующее свойство отображаемое_имя также объявленный в базовом типе.

Для нашего случая необходимо учитывать два конкретных типа, основанных на этом базовом типе: каталоги и файлы.

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.

(Данный пример кода предполагает < режа> < файла> элементы являются дочерними < папок> узла. Однако в вашем XML, < каталога>, < файла> дети элемента в < FolderName>, который выглядит как ошибка. Если это действительно преднамеренно, вам нужно будет принять примерный код соответственно.)

В соответствующем месте в исходном коде, просто вызовите:

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


3. TreeView

Теперь вам нужно только назначить коллекцию верхнего уровня записи в элемент управления 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 шаблона, а ItemsSource параметр, который связывается с ChildEntries DirectoryEntry в собственность с коллекцией дочерние записи.

Для записей в файлах DataTemplate достаточно, так как у них нет детей.

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

Теперь элемент управления TreeView должен знать только, какой из этих двух шаблонов должен использоваться для записи. Для многих случаев использование элемента управления TreeView делает это очень простым - просто добавьте эти два шаблона в локальный ресурсный словарь управления TreeView. TreeView выберет первый шаблон данных (HierarchicalDataTemplate - также шаблон данных) из своего словаря ресурсов, тип данных которого соответствует типу соответствующей записи.

В общем, полный XAML для TreeView будет выглядеть следующим образом:

<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 может быть использован вместо.)

+0

Не могли бы вы заполнить полный компилируемый код? – user2330678

+2

Извините, но нет, код примера не требует пояснений. Если вам трудно понять, тогда ваша проблема связана не с XML или TreeView, а с C#/WPF вообще. – elgonzo

Смежные вопросы