I'm trying to write code that can accept Key.Down and Key.Up and change the selection of a TreeView that is using several HierarchicalDataTemplates. In the children of a TreeViewItem, I need to get its parent so that I can determine what the next node should be selected. I noticed that TreeViewItem has a ParentTreeViewItem property, but its set to internal and therefore not exposed to access. Is there another way to emulate how to get the parent of a TreeViewItem as a TreeViewItem? Note: Parent is always null when using HierarchicalDataTemplate. Thanks in advance.
You can always use the VisualTreeHelper.GetParent to find any parent element:
private bool TryGetVisualParent<TParent>(DependencyObject element, out TParent parent) where TParent : DependencyObject
{
parent = null;
if (element is null)
{
return false;
}
element = VisualTreeHelper.GetParent(element);
if (element is TParent parentElement)
{
parent = parentElement;
return true;
}
return TryGetVisualParent(element, out parent);
}
Usage Example
private void OnTreeViewItem_Selcted(object sender, RoutedEventArgs e)
{
var selectedItem = e.OriginalSource as TreeViewItem;
if (TryGetVisualParent(selectedItem, out TreeViewItem parentItem))
{
// Handle 'parentItem'
}
}
Use TreeViewItem.Parent Property,If the parent property is null, it means there is no parent node
<TreeView>
<TreeViewItem Header="root">
<TreeViewItem Header="child" Name="child_item"></TreeViewItem>
</TreeViewItem>
</TreeView>
Related
im trying to add a button inside every item of the treeview.When the button is clicked:
- expand the tree if there are some item of the selected parent
- if not a parent: do an action.
The double action will allow me to remove completely the togglebutton.
So this is what i figured out so far:
<TreeView>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}" >
<Button Content ="{Binding Title}" Click="OnItemClick"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
My next step is to change the style of the treeview in order to have only a button that does all the work. So depending if its a parent of something or not, the button will expand for new item or will do an action inside the code.
public void OnItemClick(object sender, RoutedEventArgs args)
{
FrameworkElement parent = (FrameworkElement)((Button)sender).Parent;
if (parent is TreeViewItem)
{
//ACTION
}
else
//EXPAND
Console.WriteLine(parent);
}
So when I get the parent element of the button I want to check if the parent button (treeitemview) is expandible or not. But for now the parent I get is always null.
So the blocking step is:
is there a way do get the type of the parent of the button (treeitemview) and check if it is expandible?
Thank you
You could use the following helper method to find the parent TreeViewItem in the visual tree:
private static T FindParent<T>(DependencyObject dependencyObject) where T : DependencyObject
{
var parent = VisualTreeHelper.GetParent(dependencyObject);
if (parent == null)
return null;
var parentT = parent as T;
return parentT ?? FindParent<T>(parent);
}
Usage:
TreeViewItem parent = FindParent<TreeViewItem>((Button)sender);
if (parent != null)
//ACTION
I have a TreeView class like so, which has my own class "EntryPanel" as an ItemTemplate:
<TreeView x:Class="PowerNote.MyEntriesView"
xmlns:local="clr-namespace:PowerNote"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
ItemsSource = "{Binding viewStudents}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<local:EntryPanel/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
In my EntryPanel.xaml.cs code, I would like to access the TreeView. I tried Parent, and TemplateParent, but neither would work. How can I achieve this?
Try Doing in this way to Find the Parent.
public static T TryFindParent<T>(this DependencyObject child)
where T : DependencyObject
{
//get parent item
DependencyObject parentObject = GetParentObject(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
T parent = parentObject as T;
if (parent != null)
{
return parent;
}
else
{
//use recursion to proceed with next level
return TryFindParent<T>(parentObject);
}
}
This one will find the parent object.
public static DependencyObject GetParentObject(this DependencyObject child)
{
if (child == null) return null;
//handle content elements separately
ContentElement contentElement = child as ContentElement;
if (contentElement != null)
{
DependencyObject parent = ContentOperations.GetParent(contentElement);
if (parent != null) return parent;
FrameworkContentElement fce = contentElement as FrameworkContentElement;
return fce != null ? fce.Parent : null;
}
//also try searching for parent in framework elements (such as DockPanel, etc)
FrameworkElement frameworkElement = child as FrameworkElement;
if (frameworkElement != null)
{
DependencyObject parent = frameworkElement.Parent;
if (parent != null) return parent;
}
//if it's not a ContentElement/FrameworkElement, rely on VisualTreeHelper
return VisualTreeHelper.GetParent(child);
}
This snippet works with arbitrary dependency objects that are of Type Visual or Visual3D. So let’s say you need a reference to the Window that hosts a given Button control somewhere, all you need is this:
Button myButton = ...
Window parentWindow = UIHelper.TryFindParent<Window>(myButton);
The above TryFindParent method also makes it easy to get an item at a given position. The method below performs a hit test based on a given position. If hit testing does not return the requested item (e.g. a clicked CheckBox on a tree, while you are keen on the TreeViewItem that hosts the CheckBox), the procedure delegates the lookup to TryFindParent.
This comes in very handy for mouse-related events if you just need to now what’s under your mouse pointer:
public static T TryFindFromPoint<T>(UIElement reference, Point point)
where T:DependencyObject
{
DependencyObject element = reference.InputHitTest(point)
as DependencyObject;
if (element == null) return null;
else if (element is T) return (T)element;
else return TryFindParent<T>(element);
}
Reference
I need to find the ComboBox that a ComboBoxItem resides in.
In codebehind I catch an event when a ComboBoxItem is clicked, but I don't know which one of several ComboBoxes that the specific ComboBoxItem belongs to. How do I find the ComboBox?
Normally you can use LogicalTreeHelper.GetParent() and traverse up the logical tree from the ComboBoxItem to find the ComboBox. But this only works if the ComboBoxItems are added to the ComboBox manually, not when the items are applied to the ComboBox with databinding. When using databinding, the ComboBoxItems do not have the ComboBox as a logical parent (I don't understand why).
Any ideas?
More info:
Below is some code reconstructing my problem (not my actual code). If I would change from databinding the ComboBoxItems to setting them manually (in the XAML), the variable "comboBox" would be set to the correct ComboBox. Now comboBox is only null.
XAML:
<ComboBox Name="MyComboBox" ItemsSource="{Binding Path=ComboBoxItems, Mode=OneTime}" />
CodeBehind:
public MainWindow()
{
InitializeComponent();
MyComboBox.DataContext = this;
this.PreviewMouseDown += MainWindow_MouseDown;
}
public BindingList<string> ComboBoxItems
{
get
{
BindingList<string> items = new BindingList<string>();
items.Add("Item E");
items.Add("Item F");
items.Add("Item G");
items.Add("Item H");
return items;
}
}
private void MainWindow_MouseDown(object sender, MouseButtonEventArgs e)
{
DependencyObject clickedObject = e.OriginalSource as DependencyObject;
ComboBoxItem comboBoxItem = FindVisualParent<ComboBoxItem>(clickedObject);
if (comboBoxItem != null)
{
ComboBox comboBox = FindLogicalParent<ComboBox>(comboBoxItem);
}
}
//Tries to find visual parent of the specified type.
private static T FindVisualParent<T>(DependencyObject childElement) where T : DependencyObject
{
DependencyObject parent = VisualTreeHelper.GetParent(childElement);
T parentAsT = parent as T;
if (parent == null)
{
return null;
}
else if (parentAsT != null)
{
return parentAsT;
}
return FindVisualParent<T>(parent);
}
//Tries to find logical parent of the specified type.
private static T FindLogicalParent<T>(DependencyObject childElement) where T : DependencyObject
{
DependencyObject parent = LogicalTreeHelper.GetParent(childElement);
T parentAsT = parent as T;
if (parent == null)
{
return null;
}
else if(parentAsT != null)
{
return parentAsT;
}
return FindLogicalParent<T>(parent);
}
This is probably what you are looking for:
var comboBox = ItemsControl.ItemsControlFromItemContainer(comboBoxItem) as ComboBox;
I love how descriptive that method-name is.
On a side-note, there are some other useful methods which can be found in the property ItemsControl.ItemContainerGenerator which let you get the container associated with the templated data and vice versa.
On another side-note, you usually should not be using any of them and instead use data-binding actually.
I have two trees:
fooTree - made up of elements,
barTree - constructed by
Both trees have MouseRightButtonDown event, but the e.Source type differs:
fooTree - System.Windows.Controls.TreeViewItem
barTree - System.Windows.Controls.TreeView
Why e.Source differs? Also, how can I get the clicked item for the barTree?
Markup:
<TreeView Name="fooTree" MouseRightButtonDown="fooTree_MouseDown">
<TreeViewItem Header="foo"></TreeViewItem>
<TreeViewItem Header="foo"></TreeViewItem>
</TreeView>
<TreeView Name="barTree" MouseRightButtonDown="barTree_MouseDown" ItemsSource="{Binding BarItems}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate>
<TextBlock Text="{Binding}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Code:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
this.DataContext = this;
}
public string[] BarItems
{
get { return new string[] { "bar", "bar" }; }
}
private void barTree_MouseDown(object sender, MouseButtonEventArgs e)
{
}
private void fooTree_MouseDown(object sender, MouseButtonEventArgs e)
{
}
}
Don't know why this happens, but at least I have found a solution:
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/f0d3af69-6ecc-4ddb-9526-588b72d5196b/
If your handler is on the TreeView, use the OriginalSource property in the
event arguments and walk up the visual
parent chain until you find a
TreeViewItem. Then, select it. You can
walk the visual parent chain by using
System.Windows.Media.VisualTreeHelper.GetParent.
You could try registering a class handler for type TreeViewItem and the
mouse down event. Then, your handler
should only be called when mouse
events pass through TreeViewItem
elements.
You could register a class handler for type TreeViewItem and the context
menu opening event.
So my code is:
private void OnMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
TreeViewItem treeViewItem = VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem;
}
static DependencyObject VisualUpwardSearch<T>(DependencyObject source)
{
while (source != null && source.GetType() != typeof(T))
source = VisualTreeHelper.GetParent(source);
return source;
}
You can get the clicked item in the bartree using:
((e.Source) as TreeView).SelectedValue
But be aware that the item must actually selected first (using leftMouse). The item is not immediately selected using rightMouse...
I'm using the TreeView component from the Silverlight toolkit and I'm trying to get the parent of a selected node. The TreeView is bound to a series of objects, so directly working with a TreeViewItem appears to be out of the question.
<toolkit:TreeView SelectedItemChanged="DoStuff" DisplayMemberPath="Name" ItemsSource="{Binding MyCollection}">
<toolkit:TreeView.ItemTemplate>
<common:HierarchicalDataTemplate ItemsSource="{Binding MySubCollection}">
<StackPanel>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</common:HierarchicalDataTemplate>
</toolkit:TreeView.ItemTemplate>
</toolkit:TreeView>
Is there a way to fetch the parent of an item selected in the DoStuff event?
As long as you've downloaded the latest Silverlight Toolkit then this is easy using the TreeViewExtensions that are included.
Download the Silverlight Toolkit and install.
Add a reference to System.Windows.Controls.Toolkit (from the Silverlight Toolkit)
Use the GetParentItem() extension method, like so:
private void DoStuff(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (e.NewValue != null)
{
var parent = ((TreeView)sender).GetParentItem(e.NewValue);
//
// Do stuff with parent, this snippet updates
// a TextBlock showing the name of the current parent
if (parent != null)
{
Status.Text = parent.ToString();
}
}
}
Why don't you just have the objects in MySubcollection keep a reference to their parent?
Suppose your Tree's name is "tree", this should do the trick
((tree.SelectedItem as TreeViewItem).Parent as SomeObjectOfYours)
Justin Angel wrote about "advanced" TreeView uses, among them finding arbirtraty elements in a treeview by their bound object.
Since I am comparatively new to Silverlight, I am not sure if there is a better, more elegant way, but you could use one of his methods to find the parent.
I imagine using a call this way:
TreeViewItem trvi = ContainerFromItem(tree, tree.SelectedItem);
MySubCollectionItem parentItem = ItemFromContainer(tree, trvi.Parent); // note the "Parent" here
With the extension methods below available from somewhere:
public static TreeViewItem ContainerFromItem(this TreeView treeView, object item)
{
TreeViewItem containerThatMightContainItem = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(item);
if (containerThatMightContainItem != null)
return containerThatMightContainItem;
else
return ContainerFromItem(treeView.ItemContainerGenerator, treeView.Items, item);
}
private static TreeViewItem ContainerFromItem(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection, object item)
{
foreach (object curChildItem in itemCollection)
{
TreeViewItem parentContainer = (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(curChildItem);
if (parentContainer == null)
return null;
TreeViewItem containerThatMightContainItem = (TreeViewItem)parentContainer.ItemContainerGenerator.ContainerFromItem(item);
if (containerThatMightContainItem != null)
return containerThatMightContainItem;
TreeViewItem recursionResult = ContainerFromItem(parentContainer.ItemContainerGenerator, parentContainer.Items, item);
if (recursionResult != null)
return recursionResult;
}
return null;
}
public static object ItemFromContainer(this TreeView treeView, TreeViewItem container)
{
TreeViewItem itemThatMightBelongToContainer = (TreeViewItem)treeView.ItemContainerGenerator.ItemFromContainer(container);
if (itemThatMightBelongToContainer != null)
return itemThatMightBelongToContainer;
else
return ItemFromContainer(treeView.ItemContainerGenerator, treeView.Items, container);
}
private static object ItemFromContainer(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection, TreeViewItem container)
{
foreach (object curChildItem in itemCollection)
{
TreeViewItem parentContainer = (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(curChildItem);
if (parentContainer == null)
return null;
TreeViewItem itemThatMightBelongToContainer = (TreeViewItem)parentContainer.ItemContainerGenerator.ItemFromContainer(container);
if (itemThatMightBelongToContainer != null)
return itemThatMightBelongToContainer;
TreeViewItem recursionResult = ItemFromContainer(parentContainer.ItemContainerGenerator, parentContainer.Items, container) as TreeViewItem;
if (recursionResult != null)
return recursionResult;
}
return null;
}