좌측에 폴더가 표시될 TreeView에 임의로 값을 채워보겠습니다. 나중에는 폴더 정보를 읽어서 TreeView에 Binding 해줘야겠지요. 우선 ViewModel에서 Tree를 만들고 ViewModel:Tree를 TreeView에 Binding 해보겠습니다.
TreeView는 HierarchicalDataTemplate이라는 조금 피곤한 Template을 사용해야 합니다. 이 Template은 TreeView, Menu에서 주로 사용하는데 부모-자식으로 이루어진 데이터구조를 처리합니다.
✅ TreeView에 Data 표시하기
다중 트리
우선 다중트리를 만들어서 트리에 값이 제대로 들어가는지 확인했습니다.
[C#] - 자료구조: 다중 트리 (Multi-Tree)
위 포스팅의 Multi-Tree는 TreeView에 그대로 DataBinding은 번거롭기 때문에 ViewModel에서 사용할 Tree를 만들었습니다.
DirectoryTreeNode.cs
namespace ExplorerViewModel
{
public class DirectoryTreeNode : BaseViewModel
{
public DirectoryInfoViewModel Data { get; set; }
private ObservableCollection<DirectoryTreeNode> _children;
public ObservableCollection<DirectoryTreeNode> Children
{
get => _children;
set => OnUpdateProperty(ref _children, value);
}
[AllowNull]
public DirectoryTreeNode Parent { get; set; }
public DirectoryTreeNode(DirectoryInfoViewModel data)
{
Data = data;
_children = new ObservableCollection<DirectoryTreeNode>();
}
public void AddChild(DirectoryInfoViewModel data)
{
DirectoryTreeNode child = new DirectoryTreeNode(data);
child.Parent = this;
_children.Add(child);
}
public void RemoveChild(DirectoryInfoViewModel data)
{
}
public DirectoryTreeNode FindNode(DirectoryInfoViewModel data)
{
if (EqualityComparer<DirectoryInfoViewModel>.Default.Equals(Data, data))
{
return this;
}
foreach (var child in Children)
{
var foundNode = child.FindNode(data);
if (foundNode != null)
{
return foundNode;
}
}
return null;
}
}
}
DirectoryTree.cs
namespace ExplorerViewModel
{
public class DirectoryTree
{
public DirectoryTreeNode Root { get; set; }
public DirectoryTree(DirectoryInfoViewModel data) => Root = new DirectoryTreeNode(data);
}
}
ViewModel
UI 동일한 구조를 갖는 ViewModel을 선호합니다.
ExplorerMainViewModel 클래스가 전체 UserControl의 MainViewModel이며 TreeView의 ViewModel이 DirectoryTreeViewModel입니다. DirectoryTreeViewModel 은 Directory 구조를 가지는 DirectoryTree 클래스가 있습니다.
DirectoryTree의 Data는 DirectoryInfoViewModel 클래스입니다.
ExplorerMainViewModel.cs
public class ExplorerMainViewModel : BaseViewModel
{
private DirectoryTreeViewModel _directoryTreeVM;
public DirectoryTreeViewModel DirectoryTreeVM
{
get => _directoryTreeVM;
set => OnUpdateProperty(ref _directoryTreeVM, value, nameof(DirectoryTreeVM));
}
public ExplorerMainViewModel()
{
DirectoryTreeVM = new DirectoryTreeViewModel();
Test();
}
public void Test()
{
DirectoryTreeVM.DirectoryTreeCollection?.Root.AddChild(
new DirectoryInfoViewModel() { Name = "a" });
DirectoryTreeVM.DirectoryTreeCollection?.Root.AddChild(
new DirectoryInfoViewModel() { Name = "b" });
DirectoryTreeVM.DirectoryTreeCollection?.Root.Children
.ElementAt(0).AddChild(
new DirectoryInfoViewModel() { Name = "a.1" });
DirectoryTreeVM.DirectoryTreeCollection?.Root.Children
.ElementAt(0).AddChild(
new DirectoryInfoViewModel() { Name = "a.2" });
DirectoryTreeVM.DirectoryTreeCollection?.Root.Children
.ElementAt(1).AddChild(
new DirectoryInfoViewModel() { Name = "b.1" });
DirectoryTreeVM.DirectoryTreeCollection?.Root.Children
.ElementAt(1).AddChild(
new DirectoryInfoViewModel() { Name = "b.2" });
}
}
DirectoryTreeViewModel.cs
public class DirectoryTreeViewModel : BaseViewModel
{
private DirectoryTree? _directoryTreeCollection;
public DirectoryTree? DirectoryTreeCollection
{
get => _directoryTreeCollection;
set => OnUpdateProperty(ref _directoryTreeCollection, value, nameof(DirectoryTreeCollection));
}
public DirectoryTreeViewModel()
{
DirectoryTreeCollection = new DirectoryTree(new DirectoryInfoViewModel() { Name = "root" });
}
}
DirectoryInfoViewModel.cs
public class DirectoryInfoViewModel : BaseViewModel
{
public string? Name { get; set; }
}
xaml
<UserControl x:Class="Explorer.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Explorer"
xmlns:vm="clr-namespace:ExplorerViewModel;assembly=ExplorerViewModel"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<vm:ExplorerMainViewModel/>
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300"/>
<ColumnDefinition Width="3"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="42" />
<RowDefinition Height="*" />
<RowDefinition Height="10" />
</Grid.RowDefinitions>
<TreeView DataContext="{Binding DirectoryTreeVM}"
Grid.Row="1" Grid.RowSpan="1" Grid.Column="0" Grid.ColumnSpan="1" ItemsSource="{Binding DirectoryTreeCollection.Root.Children}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type vm:DirectoryTreeNode}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Data.Name}"/>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
<DataGrid
Grid.Row="1" Grid.Column="2" Grid.RowSpan="1" ColumnWidth="*" VerticalAlignment="Stretch" AutoGenerateColumns="False"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Center"
GridLinesVisibility="None"
SelectionMode="Extended"
SelectionUnit="FullRow">
</DataGrid>
</Grid>
</UserControl>
실행
위와 같이 코드를 생성하면 TreeView에 원하는 데이터가 표시됩니다.
TreeView에 대해서는 다음 기회에 따로 포스팅할 예정입니다.
다음 Windows 탐색기 포스팅에서는 TreeView에 실제 Directory 구조를 넣어보겠습니다.
✅ TreeView에 Data 표시하기 - 끝