[WPF] TreeView에 XElement를 속성으로 Binding
WPF의 TreeView에 XML 데이터를 그대로 흘려 보내는 방법으로서 XDocument를 적용하는 방법은 몇 가지 발견되었지만, XElement를 원 데이터로서 Binding하는 방법이 좀처럼 발견되지 않았기 때문에 메모
# 그런 틈새 일을 할 녀석이 없습니까?
상기 참고 기사와 마찬가지로 기상청의 샘플 데이터를 사용하게 한다.
기상청 방재 정보 XML 포맷 기술 자료
View 구현
기본적으로는 상기 참고 페이지와 같은 내용이 되므로, 여러가지 애용하면서, 소스 코드를 붙인다
xaml은 다음과 같습니다.
※주의 디폴트의 "MainWindow"라는 이름에서 "MainView"라는 이름으로 바꾸고 있다
MainView.xaml
<Window x:Class="WpfTestView.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfTestView"
xmlns:vm="clr-namespace:WpfTestViewModel;assembly=WpfTestViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
<Window.Resources>
<local:XAttributeConverter x:Key="XAttributeConverter" />
</Window.Resources>
<Grid>
<Border Margin="10" BorderBrush="Black" BorderThickness="1">
<!-- TreeViewのItemsSourceはIEnumerableでないといけないので、IEnumerable<XElement>-->
<TreeView ItemsSource="{Binding XTreeRoot, Mode=OneWay}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Elements}">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="TagName" Text="{Binding Name.LocalName}" />
<TextBlock x:Name="AttrStart" Text="(" />
<!-- XAttributeはBindingサポートしてないのでConverterを使う -->
<ItemsControl ItemsSource="{Binding Converter={StaticResource XAttributeConverter}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="2,0">
<TextBlock Text="{Binding Name.LocalName}" />
<TextBlock Text="="" />
<TextBlock Text="{Binding Value}" />
<TextBlock Text=""" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<TextBlock x:Name="AttrEnd" Text=")" />
<TextBlock x:Name="Separater" Text=" : " />
<TextBlock x:Name="TagValue" Text="{Binding Value}" />
</StackPanel>
<!-- ノードによって表示形式を切替え -->
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding NodeType}" Value="Text">
<Setter TargetName="TagName" Property="Text" Value="Value" />
<Setter TargetName="AttrStart" Property="Text" Value="" />
<Setter TargetName="AttrEnd" Property="Text" Value="" />
<Setter TargetName="Separater" Property="Text" Value="" />
<Setter TargetName="TagValue" Property="Text" Value="" />
</DataTrigger>
<DataTrigger Binding="{Binding HasAttributes}" Value="False">
<Setter TargetName="AttrStart" Property="Text" Value="" />
<Setter TargetName="AttrEnd" Property="Text" Value="" />
</DataTrigger>
<DataTrigger Binding="{Binding HasElements}" Value="True">
<Setter TargetName="Separater" Property="Text" Value="" />
<Setter TargetName="TagValue" Property="Text" Value="" />
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Border>
</Grid>
</Window>
주로 설명해 두어야 할 곳으로서는 코멘트에도 쓰고 있지만, XAML에 기재하고 있는 "XTreeRoot"라는 프로퍼티만이 ViewModel측과 바인드 되는 것으로, 그보다 아래 계층의 Binding 요소는 XElement 객체에 바인딩됨
HierarchicalDataTemplate 아래의 StackPanel이나 Triggers에 관해서는, 완전히 개인적인 취향으로 써 있으므로 자유롭게 커스터마이즈 가능
ViewModel 앞에 XAttribute를 바인딩하기위한 Converter를 작성하십시오.
XAttributeConverter.cs
public class XAttributeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is XElement x)) return Enumerable.Empty<XAttribute>();
return x.Attributes();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Convert에서는 XElement 객체의 IEnumerable을 반환하도록 쓰고 있다.
이번 예에서는 XML의 내용은 갱신하지 않기 때문에 TreeView는 OneWay(Source→View의 방향만)로 하고 있다
그래서 ConvertBack은 사용하지 않으므로 NotImplementedException으로 둡니다.
ViewModel 구현
MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged == null) return;
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public MainViewModel()
{
XDocument xDoc = XDocument.Load(@".\15_12_01_161130_VPWW54.xml");
XTreeRoot = new List<XElement>() { xDoc.Root };
}
private IEnumerable<XElement> _xTreeRoot = null;
public IEnumerable<XElement> XTreeRoot
{
get => _xTreeRoot;
set
{
_xTreeRoot = value;
OnPropertyChanged();
}
}
}
이제 다음과 같은 화면이 표시됩니다.
디폴트에서는 트리는 모두 닫은 상태로 시작되기 때문에, 만약 처음부터 열린 상태로 해 두고 싶은 경우,
먼저 든 참고 기사를 참조하는 방법
읽고 있는 파일은 적당히 선택하고 있다(너무 크지 않고, 너무 작지 않을 정도의 파일)
XAML의 코멘트에도 썼지만, 이하 2점이 중요. (자신이 막혔기 때문에)
* 바인딩하는 것은 IEnumerable
* XAttribute는 바인드 지원 외부이므로 변환기 사용
Reference
이 문제에 관하여([WPF] TreeView에 XElement를 속성으로 Binding), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/MtBigYashi/items/ddc587791aa602aeaf7b텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)