Find control in the visual tree - wpf

I am trying to get my SelectedRadioButton from a DataTemplate.
Wpf Inspector showed the Visual Tree:
and in code:
void menu_StatusGeneratorChanged(object sender, EventArgs e)
{
var status = Menu.Items.ItemContainerGenerator.Status;
if (status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
{
var item = Menu.Items.ItemContainerGenerator.ContainerFromIndex(0);
// item is a ContentPresenter
var control = Tools.FindChild<SelectedRadioButton>(item);
control = Tools.FindAncestor<SelectedRadioButton>(item);
}
}
item is a ContentPresenter, see the image of Wpf inspector, I believe from there I must be able to get to the SelectedRadioButton. The variable control is always null.
What am I missing here? I use these visualtreehelpers.

The code that I used to traverse the Visual Tree did not use the ApplyTemplate() method for a FrameworkElement in the tree and therefore cildren could not be found. In my situation the following code works:
/// <summary>
/// Looks for a child control within a parent by name
/// </summary>
public static DependencyObject FindChild(DependencyObject parent, string name)
{
// confirm parent and name are valid.
if (parent == null || string.IsNullOrEmpty(name)) return null;
if (parent is FrameworkElement && (parent as FrameworkElement).Name == name) return parent;
DependencyObject result = null;
if (parent is FrameworkElement) (parent as FrameworkElement).ApplyTemplate();
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
result = FindChild(child, name);
if (result != null) break;
}
return result;
}
/// <summary>
/// Looks for a child control within a parent by type
/// </summary>
public static T FindChild<T>(DependencyObject parent)
where T : DependencyObject
{
// confirm parent is valid.
if (parent == null) return null;
if (parent is T) return parent as T;
DependencyObject foundChild = null;
if (parent is FrameworkElement) (parent as FrameworkElement).ApplyTemplate();
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
foundChild = FindChild<T>(child);
if (foundChild != null) break;
}
return foundChild as T;
}
Thanks for the comments of "dev hedgehog" for pointing that out (I missed it).
I will not use this approach in production code, it has to be done with databinding like "HighCore" commented.

Related

Find logical child including hidden and collapsed nodes

I have tried finding an answer to this problem and in every post I find there is an answer to finding children recursively but none of them work with hidden or collapsed children
Also in every post someone has asked if this is possible but no one has answered so I am beginning to think this isn't possible
If anyone has a way of doing this I will be eternally grateful.
My function looks like this:
public static DependencyObject FindLogicalDescendentByName(this DependencyObject source, string name)
{
DependencyObject result = null;
IEnumerable children = LogicalTreeHelper.GetChildren(source);
foreach (var child in children)
{
if (child is DependencyObject)
{
if (child is FrameworkElement)
{
if ((child as FrameworkElement).Name.Equals(name))
result = (DependencyObject)child;
else
result = (child as DependencyObject).FindLogicalDescendentByName(name);
}
else
{
result = (child as DependencyObject).FindLogicalDescendentByName(name);
}
if (result != null)
return result;
}
}
return result;
}
-EDITED
So,
I realise my problem is that I was trying to find the item before it was created,
I was binding to a property in xaml that would go off and find an item by a given name, but the item wasnt created at that point in time, if I re-ordered the item in xaml, it works and the item is found... doh!
Here is my code, it works if you specify the X:Name and the type
Example of the call:
System.Windows.Controls.Image myImage = FindChild<System.Windows.Controls.Image>(this (or parent), "ImageName");
/// <summary>
/// Find specific child (name and type) from parent
/// </summary>
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;
}

How can you synchronise the position of multiple PanoramaItems?

I have a standard Panorama-based application, but one key item of data is repeated in two adjacent PanoramaItems.
I would like those two PanoramaItems to have the same vertical scroll position (to save the user having to find the key item again).
Is there a way to get and set the scroll position of the PanoramaItem control and detect the scroll change?
Generic solution (thanks to Paul Diston for the clues):
/// <summary>
/// Scroll each new PanoramaItem to the same position as the previous one
/// </summary>
private void Panorama_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.RemovedItems.Count > 0 && e.AddedItems.Count > 0)
{
// Add logic here if only specific PanoramaItems are required to sync
ScrollViewer firstChildAsScrollViewer =
GetChildOfType(e.RemovedItems[0] as DependencyObject, typeof (ScrollViewer)) as ScrollViewer;
ScrollViewer secondChildAsScrollViewer =
GetChildOfType(e.AddedItems[0] as DependencyObject, typeof (ScrollViewer)) as ScrollViewer;
if ((firstChildAsScrollViewer != null) && (secondChildAsScrollViewer != null))
{
secondChildAsScrollViewer.ScrollToVerticalOffset(firstChildAsScrollViewer.VerticalOffset);
}
}
}
/// <summary>
/// Bredth-first recursive check for a child of the specified type
/// </summary>
private DependencyObject GetChildOfType(DependencyObject element, Type type)
{
int count = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < count; i++)
{
var e = VisualTreeHelper.GetChild(element, i);
if (e.GetType() == type)
{
return e;
}
}
// Now try the grandchildren
for (int i = 0; i < count; i++)
{
var e = VisualTreeHelper.GetChild(element, i);
var ret = GetChildOfType(e, type);
if (ret != null)
{
return ret;
}
}
return null;
}
You could use the VisualTreeHelper to access and set the VerticalOffset of the ScrollViewer of the ListBox controls :-
private void Panorama_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
DependencyObject firstChild = VisualTreeHelper.GetChild(this.FirstListBox, 0);
ScrollViewer firstChildAsScrollViewer = firstChild as ScrollViewer;
DependencyObject secondChild = VisualTreeHelper.GetChild(this.SecondListBox, 0);
ScrollViewer secondChildAsScrollViewer = secondChild as ScrollViewer;
if ((firstChildAsScrollViewer != null) && (secondChildAsScrollViewer != null))
{
secondChildAsScrollViewer.ScrollToVerticalOffset(firstChildAsScrollViewer.VerticalOffset);
}
}
Hope this helps.
Paul Diston

How to access the element within the DataTemplate of GridViewColumnHeader?

How can I get access to an element (TextBlock) within the DataTemplate of GridViewColumnHeader from the code????
I want to set focus on the column header.
Just any GridViewColumnHeader or one in particular? You can use this code
List<GridViewColumnHeader> allGridViewColumnHeaders = GetVisualChildCollection<GridViewColumnHeader>(listView);
foreach (GridViewColumnHeader columnHeader in allGridViewColumnHeaders)
{
TextBlock textBlock = GetVisualChild<TextBlock>(columnHeader);
if (textBlock != null)
{
}
}
And the helper methods
public List<T> GetVisualChildCollection<T>(object parent) where T : Visual
{
List<T> visualCollection = new List<T>();
GetVisualChildCollection(parent as DependencyObject, visualCollection);
return visualCollection;
}
private void GetVisualChildCollection<T>(DependencyObject parent, List<T> visualCollection) where T : Visual
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if (child is T)
{
visualCollection.Add(child as T);
}
else if (child != null)
{
GetVisualChildCollection(child, visualCollection);
}
}
}
private T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
Update
To make a GridViewColumnHeader get focus you can
columnHeader.Focus();
Depending on where you do this it might not work, then you can try
EventHandler eventHandler = null;
eventHandler = new EventHandler(delegate
{
listView.LayoutUpdated -= eventHandler;
GridViewColumnHeader columnHeader = GetVisualChild<GridViewColumnHeader>(listView);
columnHeader.Focus();
});
listView.LayoutUpdated += eventHandler;
Also make sure that your GridViewColumnHeader has the following attributes
<GridViewColumnHeader IsTabStop="True" Focusable="True">

How to find child control added in a style in silverlight 4?

Hi I have ContentControl and I am applying style in which I have ListBox. I want to find ListBoxItem in Xaml.cs.
I have found out a solution. I have Implemented a method to find the visual element which takes two parameters, parent element and type of a control need to be find. Before calling this method I have used ApplyTemplate method
public static FrameworkElement[] FindDownInTree(FrameworkElement parent, Type controlType)
{
List<FrameworkElement> lst = new List<FrameworkElement>();
FindDownInTree(lst, parent, controlType);
if (lst.Count > 0)
return lst.ToArray();
return null;
}
private static void FindDownInTree(List<FrameworkElement> lstElem, DependencyObject parent, Type controlType)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
DependencyObject visual = VisualTreeHelper.GetChild(parent, i);
if (controlType.IsInstanceOfType(visual))
{
lstElem.Add(visual as FrameworkElement);
}
if (visual != null)
{
FindDownInTree(lstElem, visual, controlType);
}
}
}

How can I find WPF controls by name or type?

I need to search a WPF control hierarchy for controls that match a given name or type. How can I do this?
I combined the template format used by John Myczek and Tri Q's algorithm above to create a findChild Algorithm that can be used on any parent. Keep in mind that recursively searching a tree downwards could be a lengthy process. I've only spot-checked this on a WPF application, please comment on any errors you might find and I'll correct my code.
WPF Snoop is a useful tool in looking at the visual tree - I'd strongly recommend using it while testing or using this algorithm to check your work.
There is a small error in Tri Q's Algorithm. After the child is found, if childrenCount is > 1 and we iterate again we can overwrite the properly found child. Therefore I added a if (foundChild != null) break; into my code to deal with this condition.
/// <summary>
/// Finds a Child of a given item in the visual tree.
/// </summary>
/// <param name="parent">A direct parent of the queried item.</param>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="childName">x:Name or Name of child. </param>
/// <returns>The first parent item that matches the submitted type parameter.
/// If not matching item can be found,
/// a null parent is being returned.</returns>
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;
}
Call it like this:
TextBox foundTextBox =
UIHelper.FindChild<TextBox>(Application.Current.MainWindow, "myTextBoxName");
Note Application.Current.MainWindow can be any parent window.
You can also find an element by name using FrameworkElement.FindName(string).
Given:
<UserControl ...>
<TextBlock x:Name="myTextBlock" />
</UserControl>
In the code-behind file, you could write:
var myTextBlock = (TextBlock)this.FindName("myTextBlock");
Of course, because it's defined using x:Name, you could just reference the generated field, but perhaps you want to look it up dynamically rather than statically.
This approach is also available for templates, in which the named item appears multiple times (once per usage of the template).
You can use the VisualTreeHelper to find controls. Below is a method that uses the VisualTreeHelper to find a parent control of a specified type. You can use the VisualTreeHelper to find controls in other ways as well.
public static class UIHelper
{
/// <summary>
/// Finds a parent of a given item on the visual tree.
/// </summary>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="child">A direct or indirect child of the queried item.</param>
/// <returns>The first parent item that matches the submitted type parameter.
/// If not matching item can be found, a null reference is being returned.</returns>
public static T FindVisualParent<T>(DependencyObject child)
where T : DependencyObject
{
// get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(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 FindVisualParent<T>(parentObject);
}
}
}
Call it like this:
Window owner = UIHelper.FindVisualParent<Window>(myControl);
I may be just repeating everyone else but I do have a pretty piece of code that extends the DependencyObject class with a method FindChild() that will get you the child by type and name. Just include and use.
public static class UIChildFinder
{
public static DependencyObject FindChild(this DependencyObject reference, string childName, Type childType)
{
DependencyObject foundChild = null;
if (reference != null)
{
int childrenCount = VisualTreeHelper.GetChildrenCount(reference);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(reference, i);
// If the child is not of the request child type child
if (child.GetType() != childType)
{
// recursively drill down the tree
foundChild = FindChild(child, childName, childType);
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 = child;
break;
}
}
else
{
// child element found.
foundChild = child;
break;
}
}
}
return foundChild;
}
}
Hope you find it useful.
If you want to find ALL controls of a specific type, you might be interested in this snippet too
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject parent)
where T : DependencyObject
{
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
var childType = child as T;
if (childType != null)
{
yield return (T)child;
}
foreach (var other in FindVisualChildren<T>(child))
{
yield return other;
}
}
}
My extensions to the code.
Added overloads to find one child by type, by type and criteria (predicate), find all children of type which meet the criteria
the FindChildren method is an iterator in addition to being an extension method for DependencyObject
FindChildren walks logical sub-trees also. See Josh Smith's post linked in the blog post.
Source:
https://code.google.com/p/gishu-util/source/browse/#git%2FWPF%2FUtilities
Explanatory blog post :
http://madcoderspeak.blogspot.com/2010/04/wpf-find-child-control-of-specific-type.html
This will dismiss some elements - you should extend it like this in order to support a wider array of controls. For a brief discussion, have a look here
/// <summary>
/// Helper methods for UI-related tasks.
/// </summary>
public static class UIHelper
{
/// <summary>
/// Finds a parent of a given item on the visual tree.
/// </summary>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="child">A direct or indirect child of the
/// queried item.</param>
/// <returns>The first parent item that matches the submitted
/// type parameter. If not matching item can be found, a null
/// reference is being returned.</returns>
public static T TryFindParent<T>(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);
}
}
/// <summary>
/// This method is an alternative to WPF's
/// <see cref="VisualTreeHelper.GetParent"/> method, which also
/// supports content elements. Do note, that for content element,
/// this method falls back to the logical tree of the element!
/// </summary>
/// <param name="child">The item to be processed.</param>
/// <returns>The submitted item's parent, if available. Otherwise
/// null.</returns>
public static DependencyObject GetParentObject(DependencyObject child)
{
if (child == null) return null;
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;
}
//if it's not a ContentElement, rely on VisualTreeHelper
return VisualTreeHelper.GetParent(child);
}
}
I edited CrimsonX's code as it was not working with superclass types:
public static T FindChild<T>(DependencyObject depObj, string childName)
where T : DependencyObject
{
// Confirm obj is valid.
if (depObj == null) return null;
// success case
if (depObj is T && ((FrameworkElement)depObj).Name == childName)
return depObj as T;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
//DFS
T obj = FindChild<T>(child, childName);
if (obj != null)
return obj;
}
return null;
}
Whilst I love recursion in general, it's not as efficient as iteration when programming in C#, so perhaps the following solution is neater than the one suggested by John Myczek? This searches up a hierarchy from a given control to find an ancestor control of a particular type.
public static T FindVisualAncestorOfType<T>(this DependencyObject Elt)
where T : DependencyObject
{
for (DependencyObject parent = VisualTreeHelper.GetParent(Elt);
parent != null; parent = VisualTreeHelper.GetParent(parent))
{
T result = parent as T;
if (result != null)
return result;
}
return null;
}
Call it like this to find the Window containing a control called ExampleTextBox:
Window window = ExampleTextBox.FindVisualAncestorOfType<Window>();
Here's my code to find controls by Type while controlling how deep we go into the hierarchy
(maxDepth == 0 means infinitely deep).
public static class FrameworkElementExtension
{
public static object[] FindControls(
this FrameworkElement f, Type childType, int maxDepth)
{
return RecursiveFindControls(f, childType, 1, maxDepth);
}
private static object[] RecursiveFindControls(
object o, Type childType, int depth, int maxDepth = 0)
{
List<object> list = new List<object>();
var attrs = o.GetType()
.GetCustomAttributes(typeof(ContentPropertyAttribute), true);
if (attrs != null && attrs.Length > 0)
{
string childrenProperty = (attrs[0] as ContentPropertyAttribute).Name;
foreach (var c in (IEnumerable)o.GetType()
.GetProperty(childrenProperty).GetValue(o, null))
{
if (c.GetType().FullName == childType.FullName)
list.Add(c);
if (maxDepth == 0 || depth < maxDepth)
list.AddRange(RecursiveFindControls(
c, childType, depth + 1, maxDepth));
}
}
return list.ToArray();
}
}
exciton80... I was having a problem with your code not recursing through usercontrols. It was hitting the Grid root and throwing an error. I believe this fixes it for me:
public static object[] FindControls(this FrameworkElement f, Type childType, int maxDepth)
{
return RecursiveFindControls(f, childType, 1, maxDepth);
}
private static object[] RecursiveFindControls(object o, Type childType, int depth, int maxDepth = 0)
{
List<object> list = new List<object>();
var attrs = o.GetType().GetCustomAttributes(typeof(ContentPropertyAttribute), true);
if (attrs != null && attrs.Length > 0)
{
string childrenProperty = (attrs[0] as ContentPropertyAttribute).Name;
if (String.Equals(childrenProperty, "Content") || String.Equals(childrenProperty, "Children"))
{
var collection = o.GetType().GetProperty(childrenProperty).GetValue(o, null);
if (collection is System.Windows.Controls.UIElementCollection) // snelson 6/6/11
{
foreach (var c in (IEnumerable)collection)
{
if (c.GetType().FullName == childType.FullName)
list.Add(c);
if (maxDepth == 0 || depth < maxDepth)
list.AddRange(RecursiveFindControls(
c, childType, depth + 1, maxDepth));
}
}
else if (collection != null && collection.GetType().BaseType.Name == "Panel") // snelson 6/6/11; added because was skipping control (e.g., System.Windows.Controls.Grid)
{
if (maxDepth == 0 || depth < maxDepth)
list.AddRange(RecursiveFindControls(
collection, childType, depth + 1, maxDepth));
}
}
}
return list.ToArray();
}
I have a sequence function like this (which is completely general):
public static IEnumerable<T> SelectAllRecursively<T>(this IEnumerable<T> items, Func<T, IEnumerable<T>> func)
{
return (items ?? Enumerable.Empty<T>()).SelectMany(o => new[] { o }.Concat(SelectAllRecursively(func(o), func)));
}
Getting immediate children:
public static IEnumerable<DependencyObject> FindChildren(this DependencyObject obj)
{
return Enumerable.Range(0, VisualTreeHelper.GetChildrenCount(obj))
.Select(i => VisualTreeHelper.GetChild(obj, i));
}
Finding all children down the hiararchical tree:
public static IEnumerable<DependencyObject> FindAllChildren(this DependencyObject obj)
{
return obj.FindChildren().SelectAllRecursively(o => o.FindChildren());
}
You can call this on the Window to get all controls.
After you have the collection, you can use LINQ (i.e. OfType, Where).
Since the question is general enough that it might attract people looking for answers to very trivial cases: if you just want a child rather than a descendant, you can use Linq:
private void ItemsControlItem_Loaded(object sender, RoutedEventArgs e)
{
if (SomeCondition())
{
var children = (sender as Panel).Children;
var child = (from Control child in children
where child.Name == "NameTextBox"
select child).First();
child.Focus();
}
}
or of course the obvious for loop iterating over Children.
These options already talk about traversing the Visual Tree in C#.
Its possible to traverse the visual tree in xaml as well using RelativeSource markup extension. msdn
find by type
Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type <TypeToFind>}}}"
Here is a solution that uses a flexible predicate:
public static DependencyObject FindChild(DependencyObject parent, Func<DependencyObject, bool> predicate)
{
if (parent == null) return null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (predicate(child))
{
return child;
}
else
{
var foundChild = FindChild(child, predicate);
if (foundChild != null)
return foundChild;
}
}
return null;
}
You can for example call it like this:
var child = FindChild(parent, child =>
{
var textBlock = child as TextBlock;
if (textBlock != null && textBlock.Name == "MyTextBlock")
return true;
else
return false;
}) as TextBlock;
To find an ancestor of a given type from code, you can use:
[CanBeNull]
public static T FindAncestor<T>(DependencyObject d) where T : DependencyObject
{
while (true)
{
d = VisualTreeHelper.GetParent(d);
if (d == null)
return null;
var t = d as T;
if (t != null)
return t;
}
}
This implementation uses iteration instead of recursion which can be slightly faster.
If you're using C# 7, this can be made slightly shorter:
[CanBeNull]
public static T FindAncestor<T>(DependencyObject d) where T : DependencyObject
{
while (true)
{
d = VisualTreeHelper.GetParent(d);
if (d == null)
return null;
if (d is T t)
return t;
}
}
I can't find my control with #CrimsonX or #Drew Noakes methods, because my ControlTemplate is located in a separated XAML file.
I have found the control using the following method:
private Image backImage;
private void Diagram_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
// here is the example of the ControlTemplate declaration
//<ControlTemplate x:Key="DiagramControlTemplate1" TargetType="{x:Type Diagram}">
var ctrlTemplate = (ControlTemplate)FindResource("DiagramControlTemplate1");
// diagram is the x:Name of TemplatedControl and, "backImage" is the name of control that I want to find.
var imageControl = ctrlTemplate.FindName("backImage", diagram);
if (imageControl != null)
{
this.backImage = (Image)imageControl;
}
}
This is working for me.
This code just fixes #CrimsonX answer's bug:
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;
}
// 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
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
You just need to continue calling the method recursively if types are matching but names don't (this happens when you pass FrameworkElement as T). otherwise it's gonna return null and that's wrong.
Here are some methods that I use frequently..
Usage:
// Starts the search from thisUiElement (might be a UserContol, Window, etc..)
var combobox = thisUiElement.ChildOfType<ComboBox>();
var employeesListBox = thisUiElement.ChildOfName("EmployeesListBox");
// Starts the search from MainWindow to find the first DataGrid
var dataGrid = WpfUtils.ChildOfType<DataGrid>();
// Starts the search from MainWindow to find the all ListViews
List<ComboBox> allListViews = WpfUtils.ChildOfType<ListView>();
// Starts the search from MainWindow to find the element of name EmployeesComboBox
var combobox = WpfUtils.ChildOfName("EmployeesComboBox");
Implementation:
/*
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
namespace WpfUtilities;
*/
public static class WpfUtils{
public static Window AppMainWindow =>
Application.Current?.MainWindow;
#region Find By Type
// Start the search from MainWindow, example usage: var combobox = WpfUtils.ChildOfType<ComboBox>();
public static T ChildOfType<T>() where T : DependencyObject =>
ChildOfType<T>(AppMainWindow);
/// This will return the first child of type T
public static T ChildOfType<T>(this DependencyObject parent)
where T : DependencyObject
{
if (parent == null) return null;
T child = default;
var numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < numVisuals; i++)
{
var v = VisualTreeHelper.GetChild(parent, i);
child = v as T ?? v.ChildOfType<T>();
if (child != null)
break;
}
return child;
}
// Start the search from MainWindow, example usage: List<ComboBox> comboboxes = WpfUtils.ChildOfType<ComboBox>();
public static IEnumerable<T> ChildrenOfType<T>() where T : DependencyObject =>
ChildrenOfType<T>(AppMainWindow);
/// This will not break the search when finding the first kid of type T, but it will keep searching to return all kids of type T
public static IEnumerable<T> ChildrenOfType<T>(
this DependencyObject parent) where T : DependencyObject
{
if (parent == null) yield break;
var numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < numVisuals; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T dependencyObject)
yield return dependencyObject;
foreach (var childOfChild in child.ChildrenOfType<T>())
yield return childOfChild;
}
}
#endregion
#region Find By Name
/// If parent is null, the search will start from MainWindow, example usage: var combobox = WpfUtils.ChildOfName("EmployeesCombobox");
public static FrameworkElement ChildOfName(string childName,
DependencyObject parent = null)
{
parent ??= AppMainWindow;
object child = null;
var numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < numVisuals; i++)
{
var v = VisualTreeHelper.GetChild(parent, i);
child = v is FrameworkElement f && f.Name == childName
? f
: ChildOfName(childName, v);
if (child != null)
break;
}
return child as FrameworkElement;
}
#endregion
#region
// Yet another useful method, if you are writing code in a .xaml.cs file and you want to get the parent of a type.. example usage: this.ParentOfType<Grid>(); this.ParentOfType<UserControl>(); this.ParentOfType<Window>();
public static T ParentOfType<T>(this DependencyObject child) where T : DependencyObject
{
var parentDepObj = child;
do
{
parentDepObj = VisualTreeHelper.GetParent(parentDepObj);
if (parentDepObj is T parent) return parent;
} while (parentDepObj != null);
return null;
}
#endregion
}
I was able to find objects by name using below code.
stkMultiChildControl = stkMulti.FindChild<StackPanel>("stkMultiControl_" + couter.ToString());
Try this
<TextBlock x:Name="txtblock" FontSize="24" >Hai Welcom to this page
</TextBlock>
Code Behind
var txtblock = sender as Textblock;
txtblock.Foreground = "Red"

Resources