I have a XML databound to a TreeView with a XmlDataProvider. If i add a subnode to the XML the TreeView shows it, but how can i select this item?
XAML:
<Window.Resources>
<HierarchicalDataTemplate DataType="category" ItemsSource="{Binding XPath=child::node()}">
<TextBlock Text="{Binding XPath=@name}" FontWeight="Bold" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="card">
<TextBlock Text="{Binding XPath=./title}" FontStyle="Italic" />
</HierarchicalDataTemplate>
<XmlDataProvider x:Key="dataxml" XPath="root/cards"/>
</Window.Resources>
<TreeView Name="treeView"
ItemsSource="{Binding Source={StaticResource dataxml},
XPath=./*,
UpdateSourceTrigger=PropertyChanged}"
/>
CS:
public partial class MainWindow : Window
{
XmlDataProvider xmlDataProvider = new XmlDataProvider();
public MainWindow()
{
InitializeComponent();
xmlDataProvider = this.FindResource("dataxml") as XmlDataProvider;
xmlDataProvider.Source = new Uri(System.IO.Path.GetFullPath(fullPathToXml), UriKind.Absolute);
xmlDataProvider.Refresh();
}
public void AddChild()
{
XmlNode newNode = xmlDataProvider.Document.CreateElement("card");
XmlNode selectedItem = (XmlNode)treeView.SelectedItem;
if (selectedItem != null)
{
//add the newNode as child to the selected
selectedItem.AppendChild(newNode);
//select the childnode (newNode) ????? <=====
}
else
{
//add the newNode as child to the rootnode and select it:
xmlDataProvider.Document.DocumentElement["cards"].AppendChild(newNode);
(treeView.ItemContainerGenerator.ContainerFromItem(newNode) as TreeViewItem).IsSelected = true;
}
xmlDataProvider.Document.Save(fullPathToXml);
xmlDataProvider.Refresh();
}
}
XML:
<root>
<settings>
....
..
</settings>
<cards>
<category name="C1">
<card name="card1">
<question>bla</question>
<answer>blub</answer>
</card>
<category name="C2">
<card name="card4">
<question>bla</question>
<answer>blub</answer>
</card>
</category>
</category>
<card name="card2">
<question>bla</question>
<answer>blub</answer>
</card>
<card name="card3">
<question>bla</question>
<answer>blub</answer>
</card>
</cards>
</root>
This blog post describes how to select an item. Basically, you can't just use the TreeView.ItemContainerGenerator, as that just gives you access to the root TreeViewItems. The nested TreeViewItems are accessed using the TreeViewItem.ItemContainerGenerator of the parent TreeViewItem.
I have a first approach:
((treeView.ItemContainerGenerator.ContainerFromIndex(1) as TreeViewItem).ItemContainerGenerator.ContainerFromItem(newNode) as TreeViewItem).IsSelected = true;
this selects the newNode if i add him to the category on position 1, otherwise i become an exception ;-)
now i work on a dynamic once with more than one level :-)
It is late, so my functions arent the smoothest, but it works!
I have comment it for all that once have the same problem ;-)
Usings:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using System.Xml;
using System.Runtime.InteropServices;
Function:
/// <summary>
/// Select a XmlNode in a TreeView that is databound to a xmlDataProvider.
/// </summary>
/// <param name="treeView">A referenz to the TreeView.</param>
/// <param name="node">The node to select.</param>
public static void SelectTreeViewNode(ref TreeView treeView, XmlNode node)
{
if (treeView.HasItems)
{
//cast to xml-nodes
var xmlNodeList = treeView.Items.Cast<XmlNode>(); ;
//node at root level? -> select it
if (xmlNodeList.Contains(node))
{
(treeView.ItemContainerGenerator.ContainerFromItem(node) as TreeViewItem).IsSelected = true;
}
else
{
//get rootnode
XmlNode rootNode = GetRootNode(node, xmlNodeList);
//get a list of parent nodes
List<XmlNode> parentNodes = new List<XmlNode>();
GetAllParentNodes(rootNode, node, ref parentNodes);
parentNodes.Reverse();
//finaly, select the node
SelectNode(parentNodes, node, ref treeView, null);
}
}
}
/// <summary>
/// Goes recursiv down the parent nodes until he finds a node that is in the xmlNodeList.
/// Returns null if he can´t find anything.
/// </summary>
/// <param name="node">The start node.</param>
/// <param name="xmlNodeList">A list with possible rootnodes.</param>
/// <returns>The rootnode</returns>
private static XmlNode GetRootNode(XmlNode node, IEnumerable<XmlNode> xmlNodeList)
{
if (!xmlNodeList.Contains(node) && node.ParentNode != null)
{
return GetRootNode(node.ParentNode, xmlNodeList);
}
else if (xmlNodeList.Contains(node)) return node;
else return null;
}
/// <summary>
/// Returns all parent nodes from the actual node within the rootNode. Works recursiv.
/// </summary>
/// <param name="rootNode">The rootnode.</param>
/// <param name="actualNode">The startnode</param>
/// <param name="parentNodes">The rererenz to the outputlist</param>
private static void GetAllParentNodes(XmlNode rootNode, XmlNode actualNode, ref List<XmlNode> parentNodes)
{
if (actualNode.ParentNode != null && !actualNode.Equals(rootNode))
{
parentNodes.Add(actualNode.ParentNode);
GetAllParentNodes(rootNode, actualNode.ParentNode, ref parentNodes);
}
}
/// <summary>
/// Select a XmlNode from a TreeView that is databound to a xmlDataProvider.
/// </summary>
/// <param name="parentNodes">All the parent nodes, first in the list is the rootnode.</param>
/// <param name="node">The node to select.</param>
/// <param name="treeView">A referenz to the TreeView.</param>
/// <param name="item">Variable for the recursion.</param>
private static void SelectNode(List<XmlNode> parentNodes, XmlNode node, ref TreeView treeView, [Optional, DefaultParameterValue(null)] TreeViewItem item)
{
if (parentNodes.Count > 0)
{
TreeViewItem tvItem;
if (item != null)
tvItem = item.ItemContainerGenerator.ContainerFromItem(parentNodes.First()) as TreeViewItem;
else
tvItem = treeView.ItemContainerGenerator.ContainerFromItem(parentNodes.First()) as TreeViewItem;
parentNodes.RemoveAt(0);
SelectNode(parentNodes, node, ref treeView, tvItem);
}
else if (item != null)
{
(item.ItemContainerGenerator.ContainerFromItem(node) as TreeViewItem).IsSelected = true;
}
}