0

xmlファイルからwpfツリービューまでフォルダ構造を表示するには? Josh Smith の記事http://www.codeproject.com/Articles/26288/Simplifying-the-WPF-TreeView-by-Using-the-ViewModelの 2 番目の部分 (リージョン) を試しましたが、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以下は私の失敗した試みです。mvvm パターンを使用して wpf ツリービューにバインドするために、xml ファイルの内容をリストに保存しようとしています。

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

XML ファイルのデータを TreeView に表示するには、データをデータ モデルに編成します。これにより、TreeView を介した操作と表現が容易になります。


1. データモデル

ツリー内のすべてのエントリは、共通の基本型から派生します。

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

すべてのエントリは名前を表示する必要があるため、適切なプロパティDisplayNameも基本型内で宣言されます。

この場合、その基本型から派生する 2 つの具象型 (ディレクトリとファイル) を考慮する必要があります。

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

実際のコードでは、コード ビハインドでItemsSourceプロパティを設定する代わりに、おそらく XAML でデータ バインディングを使用します。

ここまでの作業で、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パラメータに注意してください。

ファイル エントリには子がないため、 DataTemplateで十分です。

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

ここで、TreeView コントロールは、これら 2 つのテンプレートのどちらがどのエントリに使用されるかを知るだけで済みます。多くのユース ケースでは、TreeView コントロールを使用すると、これが非常に簡単になります。これら 2 つのテンプレートを 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 に答える