web-dev-qa-db-ja.com

wpfのファイルパスのリストからツリービューに入力する

this または this other の例のように、ファイルパスのコレクションからツリービューを生成する方法の例がいくつかあります。私はそのようなWPFの例を見つけることができないようです。私はWindowsフォームを統合し、それを機能させるために別のコントロールを使用できることを知っていますが、wpf treeviewコントロールで同じことができればいいでしょう。構築したいツリービューは約5万のファイルで構成されているので、何かにバインドした方がいいと思います。しかし、最初にそれをバインドする前に、文字列のリスト(文字列にはファイルのパスが含まれています)に基づいてそれを構築すると役立つと思います。

28
Tono Nam

私はその質問に興味をそそられ、これを一緒に投げました。最初のパスとして、私はあなたが探しているものにかなり近いと思います。 50,000アイテムについて話すと、遅延読み込みが適切かもしれないと思います。とにかく、これは、Josh Smithによる article に基づくシンプルなバージョンです。私はすべてのコードをここに配置しましたが、魔法は実際にデータテンプレートで行われます。

使用しているオブジェクトを表すいくつかのクラスがあるとすると...

using System.Collections.Generic;

namespace WpfTreeViewBinding.Model
{
    public class Item
    {
        public string Name { get; set; }
        public string Path { get; set; }
    }
}

そして...

namespace WpfTreeViewBinding.Model
{
    public class FileItem : Item
    {

    }
}

そして...

namespace WpfTreeViewBinding.Model
{
    public class DirectoryItem : Item
    {
        public List<Item> Items { get; set; }

        public DirectoryItem()
        {
            Items = new List<Item>();
        }
    }
}

私はいくつかのディレクトリ/ファイルをロードするための再帰的なメソッドを作成しました...

using System.Collections.Generic;
using System.IO;
using WpfTreeViewBinding.Model;

namespace WpfTreeViewBinding
{
    public class ItemProvider
    {
        public List<Item> GetItems(string path)
        {
            var items = new List<Item>();

            var dirInfo = new DirectoryInfo(path);

            foreach(var directory in dirInfo.GetDirectories())
            {
                var item = new DirectoryItem
                               {
                                   Name = directory.Name,
                                   Path = directory.FullName,
                                   Items = GetItems(directory.FullName)
                               };

                items.Add(item);
            }

            foreach(var file in dirInfo.GetFiles())
            {
                var item = new FileItem
                               {
                                   Name = file.Name, 
                                   Path = file.FullName
                               };

                items.Add(item);
            }

            return items;
        }
    }
}

そこからは、データを取得するだけです...

using System.Windows;

namespace WpfTreeViewBinding
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            var itemProvider = new ItemProvider();

            var items = itemProvider.GetItems("C:\\Temp");

            DataContext = items;
        }
    }
}

そしてそれを表示しています...

<Window x:Class="WpfTreeViewBinding.MainWindow"
        xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml" 
        xmlns:Model="clr-namespace:WpfTreeViewBinding.Model" 
        Title="MainWindow" 
        Height="350" Width="525">

    <Window.Resources>

        <HierarchicalDataTemplate DataType="{x:Type Model:DirectoryItem}"
                                  ItemsSource="{Binding Items}">
            <TextBlock Text="{Binding Path=Name}" ToolTip="{Binding Path=Path}" />
        </HierarchicalDataTemplate>

        <DataTemplate DataType="{x:Type Model:FileItem}">
            <TextBlock Text="{Binding Path=Name}" ToolTip="{Binding Path=Path}" />
        </DataTemplate>

    </Window.Resources>

    <Grid Margin="8">
        <TreeView ItemsSource="{Binding}" />
    </Grid>

</Window>

魔法のすべては、実際にデータテンプレートで発生します。全体の鍵は、階層を持つすべてのアイテム(つまりディレクトリ)にHierarchicalDataTemplateを使用することだと思います。

注1:私はこれを広範囲にテストしていません。パフォーマンスについてはプロファイルされていません。これは私がずっと前に解決しようとして諦めた問題なので、どんなフィードバックでも歓迎します。ありがとう!

注2:ハードコーディングされたパスを、システムで意味のあるものに設定する必要があります。

これは、さまざまなレベルのディレクトリとファイルを示すスクリーンショットです...

enter image description here

62
John Laffoon

以前のソリューションの小さな拡張機能:アイコンをサポートするためのxamlコードを追加し、開いたフォルダーと閉じたフォルダーのアイコンを切り替えるサポートを追加しました。

 <HierarchicalDataTemplate DataType="{x:Type viewModels:SourceControlDirecoryViewModel}"
                                  ItemsSource="{Binding Items}">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="5" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Image Width="16"
                       Height="16"
                       Source="{StaticResource ImageSourceFolderClosed16x16}"
                       x:Name="img" />
                <TextBlock Text="{Binding Path=Name}"
                           ToolTip="{Binding Path=Path}"
                           Grid.Column="2" />
            </Grid>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding IsExpanded, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TreeViewItem}}}"
                             Value="True">
                    <Setter Property="Source"
                            TargetName="img"
                            Value="{StaticResource ImageSourceFolderOpened16x16}" />
                </DataTrigger>
            </DataTemplate.Triggers>
        </HierarchicalDataTemplate>
4
Mr.B