TreeView が配置された UI で Model-View-ViewModel パターンを使う方法(VB.NET)
TreeView にどう再帰的にデータを設定するか調べていたら、id:griefworker さんの以下の記事が見つかりました。
TreeView が配置された UI で Model-View-ViewModel パターンを使う方法
なるほど!HierarchicalDataTemplate を使えばいいわけですね!*1
以下学習のため、Livet のテンプレートで生成した VB.NET プロジェクトに実装してみました。*2
ViewModel
まず ViewModel です。Livet を使う関係上、id:griefworker さんのコードをかなり改造させていただいてます。再帰構造にするため、自身のコレクションをメンバに持たせることが肝ですね。
Option Explicit On Option Strict On Imports System.Collections.Generic Imports System.Collections.ObjectModel Imports System.IO Public Class DirectoryViewModel Inherits ViewModel Private _children As ReadOnlyCollection(Of DirectoryViewModel) Private Shared _dummy As DirectoryViewModel = New DirectoryViewModel() Private _model As DirectoryInfo Private _parent As DirectoryViewModel #Region "プロパティ" Public ReadOnly Property Children As ReadOnlyCollection(Of DirectoryViewModel) Get If (Me.HasDummy) Then Dim list = New List(Of DirectoryViewModel)() Try For Each info In _model.GetDirectories() Dim dic = New DirectoryViewModel() With {.Path = info.FullName} list.Add(dic) Next Catch ex As UnauthorizedAccessException Catch ex As Exception Throw End Try _children = New ReadOnlyCollection(Of DirectoryViewModel)(list) End If Return _children End Get End Property Public ReadOnly Property HasDummy() As Boolean Get Return (_children.Count = 1) AndAlso (Object.ReferenceEquals(_children(0), _dummy)) End Get End Property Public ReadOnly Property Name As String Get Return _model.Name End Get End Property Public ReadOnly Property Parent() As DirectoryViewModel Get Return _parent End Get End Property #Region "IsSelected変更通知プロパティ" Private _IsSelected As Boolean Public Property IsSelected() As Boolean Get Return _IsSelected End Get Set(ByVal value As Boolean) If (_IsSelected = value) Then Return _IsSelected = value RaisePropertyChanged("IsSelected") End Set End Property #End Region #Region "Path変更通知プロパティ" Private _Path As String Public Property Path() As String Get Return _Path End Get Set(ByVal value As String) If (_Path = value) Then Return _Path = value RaisePropertyChanged("Path") Me.SetDirectory(Me, _Path) End Set End Property #End Region #End Region #Region "メソッド" Public Sub SetDirectory(parent As DirectoryViewModel, path As String) If (Directory.Exists(path) = False) Then Throw New ArgumentException(path + " は存在しません。", "path") End If _model = New DirectoryInfo(path) _parent = parent _children = New ReadOnlyCollection(Of DirectoryViewModel)(New DirectoryViewModel() {_dummy}) End Sub #End Region End Class
View
お次は View です。コードビハインドをなくすため、DirectoryViewModel.Path プロパティの最上位ディレクトリの設定を XAML に移動しました。
<Window x:Class="DirectoryWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" xmlns:l="http://schemas.livet-mvvm.net/2011/wpf" xmlns:local="clr-namespace:DirectoryApplication" Title="DirectoryWindow" Height="350" Width="525"> <Window.DataContext> <local:DirectoryViewModel Path="C:\" /> </Window.DataContext> <Grid> <!--DirectoryViewModel の Children を TreeView にバインド--> <TreeView ItemsSource="{Binding Path=Children}"> <!--TreeViewItem の状態を DirectoryViewModel に保存するためのスタイル--> <TreeView.ItemContainerStyle> <Style TargetType="{x:Type TreeViewItem}"> <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}"/> <Setter Property="FontWeight" Value="Normal"/> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="FontWeight" Value="Bold"/> </Trigger> </Style.Triggers> </Style> </TreeView.ItemContainerStyle> <!--DirectoryViewModel のデータを表示するときに使うテンプレート--> <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding Path=Children}"> <TextBlock Text="{Binding Path=Name}"/> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> </Grid> </Window>
なんかのお役に立てば幸いです。<(_ _)>