SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

UWPアプリ開発の最前線

UWPアプリでファイルマネージャーを作ろう!
~ついにWindows 10 1803で実現

UWPアプリ開発の最前線 第8回

ルートフォルダーを取得してTreeViewコントロールに表示する

 それでは、ファイルマネージャーの骨格を作っていきましょう。まず、起動時にはすべてのルートフォルダーを表示したいですね。ルートフォルダーをStorageFolderオブジェクトとして取得し、それをTreeViewコントロールに追加します。

TreeViewコントロールを配置する

 TreeViewコントロールは標準のものですから、次のコードのようにXAMLを記述するだけです。

TreeViewコントロールを配置する(MainPage.xaml
<TreeView x:Name="TreeView1" SelectionMode="Single"
          />

 選択できるノード(ここではフォルダーやファイル)は1つだけに制限したいので、SelectionModeプロパティをSingleにしています。この後で、イベントハンドラーやスタイルを追加していきます。

ルートフォルダーを取得してTreeViewコントロールに追加する

 前述したようにDirectory.GetLogicalDrivesメソッドで全ドライブレターが得られるので、StorageFolder.GetFolderFromPathAsyncメソッドを使って各ルートフォルダーのStorageFolderオブジェクトを取得します。そして、取得したStorageFolderオブジェクトを持つTreeViewNodeオブジェクトを作り、TreeViewのルートノードに追加します(次のコード)。

ルートフォルダーを取得してTreeViewコントロールに追加する(MainPage.xaml.cs
private async Task InitializeTreeViewAsync()
{
  // すべてのドライブレターを取得する
  string[] drives = System.IO.Directory.GetLogicalDrives();

  foreach (var drive in drives)
  {
    // 各ドライブのルートフォルダーを取得する
    StorageFolder folder
      = await StorageFolder.GetFolderFromPathAsync(drive);

    // TreeViewのルートノードに追加する
    this.TreeView1.RootNodes.Add(new TreeViewNode()
    {
      IsExpanded = false,
      HasUnrealizedChildren = true,
      Content = folder,
    });
  }
}

 ここでTreeViewNodeオブジェクトのIsExpandedプロパティは、ノードが展開された状態かどうかを示します。HasUnrealizedChildrenプロパティは、trueにすると、実際に子ノードがあるかどうかに関係なくノードを展開するUIを表示します。

 上のメソッドを画面表示時に呼び出すようにして実行してみると、次の画像のようになります。

取得したルートフォルダーのオブジェクトをそのままTreeViewに表示した
取得したルートフォルダーのオブジェクトをそのままTreeViewに表示した

 まだデータテンプレートを与えていないので、オブジェクトの型名が表示されています。2つあるうちの下側を選択した状態です。TreeViewコントロールのSelectionModeプロパティをSingleにしたので、複数を選択することはできません。それぞれの左側に表示されている「>」記号は、そのノードに子ノードがあることを示しています。ルートフォルダー配下のフォルダー/ファイルを実際にはまだ追加していませんが、HasUnrealizedChildrenプロパティをtrueにしたので、あたかも子ノードがあるかのような表示になっています。

TreeViewコントロールの表示をカスタマイズする

 TreeViewのノードにデータテンプレートを適用して、フォルダー名/ファイル名とフォルダー/ファイルのアイコンを表示しましょう。とはいうものの、Windows 10 1803のTreeViewコントロールには、その子要素であるノード(TreeViewNodeオブジェクト)に適用するデータテンプレートを指定するためのプロパティが用意されていません(本稿執筆時点ではそのためのItemTemplateプロパティはプレビュー段階)。そのため、TreeViewに与えるスタイル定義の中でデータテンプレートを指定するという、少々煩雑な手順を踏みます。

フォルダー用/ファイル用のデータテンプレート

 TreeViewのノードには、StorageFolderオブジェクトだけでなく、このあとStorageFileオブジェクトも追加します。見た目で区別できるように、アイコンが異なる2種類のデータテンプレートを用意します(次のコード)。

フォルダー用/ファイル用のデータテンプレート(MainPage.xaml
<!-- TreeViewのノードに適用するデータテンプレート -->
<!-- フォルダー用 -->
<DataTemplate x:Key="FolderTemplate">
  <TreeViewItem IsHitTestVisible="False">
    <StackPanel Orientation="Horizontal">
      <Image Width="20" Source="Assets/folder.png"/>
      <TextBlock Text="{Binding Content.Name}" FontWeight="Bold" />
    </StackPanel>
  </TreeViewItem>
</DataTemplate>
<!-- ファイル用 -->
<DataTemplate x:Key="FileTemplate">
  <TreeViewItem IsHitTestVisible="False">
    <StackPanel Orientation="Horizontal">
      <Image Width="20" Source="Assets/file.png"/>
      <TextBlock Text="{Binding Content.Name}" />
    </StackPanel>
  </TreeViewItem>
</DataTemplate>

 この2つのテンプレートは、ほぼ同じです。違いは、アイコンと、フォルダーの文字列を太字にしていることくらいです。

 データコンテキストはTreeViewNodeオブジェクトになるので、そのContentプロパティ(=IStorageItemオブジェクト)のNameプロパティ(=フォルダー名/ファイル名)をTextBlcokコントロールにデータバインディングしています。

データテンプレートセレクター

 上の2つのテンプレートを切り替える仕掛けを用意します。

 まず、DataTemplateSelectorクラスを継承して、テンプレートを切り替えるためのクラスを作ります(次のコード)。

テンプレートを切り替えるためのクラス(StorageItemTemplateSelector.cs
public class StorageItemTemplateSelector : DataTemplateSelector
{
  // 選択対象のデータテンプレート
  // その実体はXAML側からセットされる
  public DataTemplate FolderTemplate { get; set; }
  public DataTemplate FileTemplate { get; set; }

  // 渡されたTreeViewNodeのContentがフォルダーだったらFolderTemplateを返す。
  // それ以外のときはFileTemplateを返す。
  protected override DataTemplate SelectTemplateCore(object item)
    => ((item as TreeViewNode)?.Content is StorageFolder)
        ? FolderTemplate : FileTemplate;
}

 このStorageItemTemplateSelectorクラスのインスタンス化は、XAML側で行います。ページのリソースに次のコードを追加します。

データテンプレートセレクターをインスタンス化する(MainPage.xaml
<local:StorageItemTemplateSelector
        x:Key="StorageItemTemplateSelector"
        FolderTemplate="{StaticResource FolderTemplate}"
        FileTemplate="{StaticResource FileTemplate}" />

 XAMLで、StorageItemTemplateSelectorオブジェクトを生成し、そのFolderTemplateプロパティ/FileTemplateプロパティにデータテンプレートのオブジェクトをセットしています。

TreeViewのスタイル定義

 まず、generic.xamlからTreeView用のスタイルをコピーして、ページのリソースに貼り付けます。generic.xamlを開くには、MainPage.xamlの冒頭部分にあるBackgroundプロパティのApplicationPageBackgroundThemeBrush部分にカーソルを置いて[F12]キー(定義へ移動)を押すと速いです。generic.xamlの中で、「TreeView」を単語単位で検索してスタイル定義を見つけます。

 そうしたら、次のコードのようにTreeView用のスタイルを変更します(変更部分を太字にしてあります)。

TreeViewに適用するスタイル定義(MainPage.xaml
<Style TargetType="TreeView">
  <Setter Property="IsTabStop" Value="False" />
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="TreeView">
        <TreeViewList x:Name="ListControl"
            ItemTemplateSelector="{StaticResource StorageItemTemplateSelector}"
            ItemContainerStyle="{StaticResource TreeViewItemStyle}"
            CanDragItems="False" AllowDrop="False" CanReorderItems="False">
          <TreeViewList.ItemContainerTransitions>
            <TransitionCollection>
              <ContentThemeTransition />
              <ReorderThemeTransition />
              <EntranceThemeTransition IsStaggeringEnabled="False" />
            </TransitionCollection>
          </TreeViewList.ItemContainerTransitions>
        </TreeViewList>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

 これでStorageItemTemplateSelectorオブジェクトがTreeViewにセットされました。StorageItemTemplateSelectorオブジェクトは、前述したようにノードの内容に合わせてフォルダー用またはファイル用のデータテンプレートを提供してくれます。実行してみると、次の画像のようにルートフォルダーが表示されます。なお、画像中のPドライブというのは、この記事のために用意したネットワークドライブです。

TreeViewのノードにルートフォルダーの名前とアイコンが表示された
TreeViewのノードにデータテンプレートを適用した

次のページ
TreeViewのノードがタップされたら展開する

関連リンク

この記事は参考になりましたか?

UWPアプリ開発の最前線連載記事一覧

もっと読む

この記事の著者

biac(ばいあっく)

HONDA R&Dで自動車の設計をやっていた機械屋さんが、技術の進化スピードに魅かれてプログラマーに。以来30年ほど、より良いコードをどうやったら作れるか、模索の人生。わんくま同盟の勉強会(名古屋)で、よく喋ってたりする。2014/10~2019/6 Microsoft MVP (Windows Devel...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/11072 2018/09/27 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー