Reputation: 1031
I am working on a genealogy application and I plan to store references to each family member in an XML document. I would like to read this document and display it as a treeview in my application and then use it to display a family member elsewhere in the application. What I need to do is find a specific value in the node tree (each member will have a unique value). How would I find this value? Could you please give me a hand here, I am quite new to node editing. TY.
BTW, below is my XAML code:
<HierarchicalDataTemplate x:Key="NodeTemplate">
<HierarchicalDataTemplate.ItemsSource>
<Binding XPath="child::*" />
</HierarchicalDataTemplate.ItemsSource>
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
<XmlDataProvider x:Key="xmlDataProvider"></XmlDataProvider>
</Window.Resources>
<Grid>
<TreeView Margin="0,24,0,143"
Name="treeView1"
Background="AliceBlue"
ItemsSource="{Binding Source={StaticResource xmlDataProvider}, XPath=*}"
ItemTemplate= "{StaticResource NodeTemplate}"/>
Upvotes: 0
Views: 3905
Reputation: 96730
When you bind a selectable ItemsControl
to an XmlDataProvider
, the SelectedItem
contains the XmlNode
that's the binding source for the item. If you set that as the DataContext
for a view, you can then display (or edit) anything that can be selected via XPath from that node.
So in the below example, which you can paste into Kaxaml, the ContentControl
's DataContext
is bound to the SelectedItem
on the TreeView
. Controls placed inside it can then bind to any XML nodes that are findable via XPath.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<XmlDataProvider x:Key="Data" XPath="/Data">
<x:XData>
<Data xmlns="">
<Person Name="Becky">
<Person Name="Stephen">
<Person Name="Tom"/>
<Person Name="Susannah"/>
</Person>
<Person Name="Deborah">
<Person Name="Geoffrey"/>
<Person Name="Christopher"/>
</Person>
</Person>
</Data>
</x:XData>
</XmlDataProvider>
</Page.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TreeView x:Name="MyTreeView" ItemsSource="{Binding Source={StaticResource Data}, XPath=*}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding XPath=*}">
<TextBlock Text="{Binding XPath=@Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<ContentControl Grid.Column="1" DataContext="{Binding ElementName=MyTreeView, Path=SelectedItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Row="0">Parent:</Label>
<Label Grid.Row="1">Children:</Label>
<Label Grid.Column="1" Content="{Binding XPath=@Name}"/>
<ListBox Grid.Row="1" Grid.Column="1" ItemsSource="{Binding XPath=*}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding XPath=@Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</ContentControl>
</Grid>
</Page>
You have to use a ContentControl
and set the DataContext
. That's the only actual trick here. The reason you have to do this: if you tried to just bind a TextBlock
, say, you'd end up with a control like:
<TextBlock Text="{Binding ElementName=MyTreeView, Path=SelectedItem, XPath=@Name}"/>
and you can't use Path
and XPath
in the same binding.
Upvotes: 0
Reputation: 14037
I've assumed that the unique value is the id
attribute. So the code will look so:
private void Button_Click(object sender, RoutedEventArgs e)
{
var item = FindItemById(treeView1.Items.Cast<XmlElement>(), "2.1");
if (item != null)
{
//Do something...
//for example, item.SetAttribute("name", "test");
//But this code will not work if the item isn't visible
//var container = treeView1.ItemContainerGenerator.ContainerFromItem(item);
}
}
public XmlElement FindItemById(IEnumerable<XmlElement> items, string id)
{
foreach (var item in items)
{
if (item.HasAttribute("id") && item.GetAttribute("id") == id)
return item;
var childItemsResult = FindItemById(item.ChildNodes.Cast<XmlElement>(), id);
if (childItemsResult != null)
return childItemsResult;
}
return null;
}
If your xml file has a different attribute as an identifier, change this line accordingly: item.HasAttribute("id") && item.GetAttribute("id")
.
The final result will be the XmlElement
object. But it isn't easy to retrieve the container because of the algorithm of generation of treeview items. Anyway, if you have a specific question - I can help to build a correct architecture.
Upvotes: 1