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
Related
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>
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'm struggling a bit with keyboard navigation in the Telerik WPF RadCarousel. If i click outside an item, but within the carousel-control, keyboard naviation works as expected (I can switch between items using the left and right keyboard arrows), but if I click an item within the RadCarousel, keyboard navigation is gone. How can I get the RadCarousel to handle keyboard navigation when an item in the carousel has focus?
Additional things I want to accomplish:
Automatically show the SelectedItem as the "front-item" in the carousel.
Automatically select the "front-item" when navigating through the carousel.
My RadCarousel binding is set up as follows:
<ScrollViewer CanContentScroll="true">
<telerik:RadCarousel Name="carousel" HorizontalScrollBarVisibility="Hidden"
ItemsSource="{Binding Path=Templates}"
ItemTemplate="{StaticResource template}"
SelectedItem="{Binding Path=SelectedTemplateAndFolder}" />
</ScrollViewer>
Edit:
By using Snoop, I can see that the "CarouselScrollViewer" has focus when the scrolling is working. Selecting an item causes the RadCarousel to get focus (and the navigation to stop working).
I got the most import things to work, which was the keyboard navigation and moving the selected item to the front of the carousel.
Keyboard navigation:
private void Carousel_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
CarouselScrollViewer scrollViewer = FindChild<CarouselScrollViewer>(this.carousel, null);
scrollViewer.Focus();
}
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
Moving selected item into center of carousel:
private void Carousel_SelectionChanged(object sender, SelectionChangeEventArgs e)
{
this.carousel.BringDataItemIntoView(this.carousel.CurrentItem);
}
I have a DataGrid defined with WPF Toolkit. The CellEditingTemplate of this DataGrid is associated at runtime with a custom function that build a FrameworkElementFactory element.
Now I have to access to control that is inserted inside DataTemplate of CellEditingTempleta, but I do not know how to do.
On web I found a useful ListView Helper...
public static class ListViewHelper
{
public static FrameworkElement GetElementFromCellTemplate(ListView listView, Int32 column, Int32 row, String name)
{
if (row >= listView.Items.Count || row < 0)
{
throw new ArgumentOutOfRangeException("row");
}
GridView gridView = listView.View as GridView;
if (gridView == null)
{
return null;
}
if (column >= gridView.Columns.Count || column < 0)
{
throw new ArgumentOutOfRangeException("column");
}
ListViewItem item = listView.ItemContainerGenerator.ContainerFromItem(listView.Items[row]) as ListViewItem;
if (item != null)
{
GridViewRowPresenter rowPresenter = GetFrameworkElementByName<GridViewRowPresenter>(item);
if (rowPresenter != null)
{
ContentPresenter templatedParent = VisualTreeHelper.GetChild(rowPresenter, column) as ContentPresenter;
DataTemplate dataTemplate = gridView.Columns[column].CellTemplate;
if (dataTemplate != null && templatedParent != null)
{
return dataTemplate.FindName(name, templatedParent) as FrameworkElement;
}
}
}
return null;
}
private static T GetFrameworkElementByName<T>(FrameworkElement referenceElement) where T : FrameworkElement
{
FrameworkElement child = null;
for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceElement); i++)
{
child = VisualTreeHelper.GetChild(referenceElement, i) as FrameworkElement;
System.Diagnostics.Debug.WriteLine(child);
if (child != null && child.GetType() == typeof(T))
{
break;
}
else if (child != null)
{
child = GetFrameworkElementByName<T>(child);
if (child != null && child.GetType() == typeof(T))
{
break;
}
}
}
return child as T;
}
}
this code work with the ListView object but not with the DataGrid object.
How can use something like this in DataGrid?
Well, but I created a DataTemplate with this code...
var txtStandard = new FrameworkElementFactory(typeof(TextBox)); txtStandard.SetBinding(TextBox.TextProperty, new Binding("Entity")); new DataTemplate { VisualTree = txtStandard };
and I need to manage the txtStandard like a Control object; how can cast a FrameworkElementFactory to Control?
New Idea:
Instead of creating a text object in the DataTemplate, create an instance of a custom control you create. Then put the text object in your custom control and put all the code you need to manage it in the custom control.
Old Idea:
You will need to recurse through the DataGrid's VisualTree. I recommend you spy on your app at run time with a program such as Snoop and then edit the sample code you provided to travel down the correct path to the control you are interested in.
Keep in mind that this is hard because it is not a common workflow. You should probably be creating bindings in your DataTemplate instead.
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;
}