[WPF]TreeViewにXElementを属性付きBinding
C#初学者用記事をいろいろ書き込んでる最中ですが、自分の為の備忘録。
WPFのTreeViewにXMLデータをそのまま流し込む方法として、XDocumentを適用する方法はいくつか見つかったものの、XElementを元データとしてBindingする方法がなかなか見つからなかったのでメモ
# そんなニッチなことするやつあんまいないのか
- XDocumentを元データする参考記事
上記参考記事と同様に、気象庁のサンプルデータを使わせてもらうことにする
気象庁防災情報XMLフォーマット 技術資料
Viewの実装
基本的には上記参考ページと同じ内容となるので、諸々割愛しつつ、ソースコードを貼る
xamlは以下の通り。
※注意 デフォルトの"MainWindow"という名前から"MainView"という名前に変えている
<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を書いておく
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の実装
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はバインドサポート外なのでconverter使う
Author And Source
この問題について([WPF]TreeViewにXElementを属性付きBinding), 我々は、より多くの情報をここで見つけました https://qiita.com/MtBigYashi/items/ddc587791aa602aeaf7b著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .