How can I unit test something that uses VisualTreeHelper? - wpf

I have this static helper function:
public static DependencyObject GetParentObject(DependencyObject child)
{
if (child == null) return null;
ContentElement contentElement = child as ContentElement;
if (contentElement != null)
{
var parent = ContentOperations.GetParent(contentElement);
if (parent != null) return parent;
var fce = contentElement as FrameworkContentElement;
return fce != null ? fce.Parent : null;
}
//if it's not a ContentElement, rely on VisualTreeHelper
return VisualTreeHelper.GetParent(child);
}
It works in a real application, but I'm trying to write some unit tests for it. Here's my first attempt:
[Test]
public void GetParentObject_returns_immediate_parent()
{
var contentControl = new ContentControl();
var textBox = new TextBox();
contentControl.BeginInit();
contentControl.Content = textBox;
contentControl.EndInit();
var result = UIHelper.GetParentObject(textBox);
Assert.AreSame(contentControl, result);
}
Unfortunately it fails because VisualTreeHelper is returning null. How can I mock up a visual tree that will work?

Based on this answer here on printing documents via Wpf-controls and convert to XPS I came up with the following extension method to create the visual tree. It works well within NUnit without STA-thread or anything.
/// <summary>
/// Render a UIElement such that the visual tree is generated,
/// without actually displaying the UIElement
/// anywhere
/// </summary>
public static void CreateVisualTree(this UIElement element)
{
var fixedDoc = new FixedDocument();
var pageContent = new PageContent();
var fixedPage = new FixedPage();
fixedPage.Children.Add(element);
pageContent.ToMaybeOf<IAddChild>().Do(c => c.AddChild(fixedPage));
fixedDoc.Pages.Add(pageContent);
var f = new XpsSerializerFactory();
var w = f.CreateSerializerWriter(new MemoryStream());
w.Write(fixedDoc);
}
Please note that
the other answer uses an API of the Reach-dll that does not look like the API I am seeing. I assume that there are differences between .NEt Framework versions 3.5 and 4.0
the ToMaybeOf stuff basically means to treat pageContent as IAddChild and do an action on that interface
this will not work with an element of type Window since the element is essentially added as a child to a Visual and Window will complain bitterly about this.

This is why statics are problematic.
You can abstract the functionality behind an interface and create a default implementation that uses the static method. You can then use dependency injection, which makes this unit test trivial -- mock the dependency on IVisualTreeHelper or roll your own stub implementation that you can configure to return any value you assign.
public class Foo
{
static IVisualTreeHelper visualTreeHelper;
static Foo()
{
Foo.visualTreeHelper = new FrameworkVisualTreeHelper();
}
public Foo(IVisualTreeHelper visualTreeHelper)
{
Foo.visualTreeHelper = visualTreeHelper;
}
public static DependencyObject GetParentObject(DependencyObject child)
{
if (child == null) return null;
ContentElement contentElement = child as ContentElement;
if (contentElement != null)
{
var parent = ContentOperations.GetParent(contentElement);
if (parent != null) return parent;
var fce = contentElement as FrameworkContentElement;
return fce != null ? fce.Parent : null;
}
//if it's not a ContentElement, rely on the IVisualTreeHelper
return visualTreeHelper.GetParent(child);
}
}
public interface IVisualTreeHelper
{
DependencyObject GetParent(DependencyObject reference);
}
public class FrameworkVisualTreeHelper : IVisualTreeHelper
{
public DependencyObject GetParent(DependencyObject reference)
{
return VisualTreeHelper.GetParent(reference);
}
}
Obviously, you may need to add other VisualTreeHelper methods to your interface and default implementation, if you're using other methods elsewhere.
It is still not completely clean because the unit you're testing is itself static, and you're going to run into exactly the same problem when you try to unit test any class that relies on your UIHelper class' static methods.

To mock a visual tree you will have to actually create and render one. So you will have to create an actual window, wich isn't particulary ideal for a unit test.

Related

Get submenu popup window when opened?

I have a tree of MenuItem's in my xaml and I need to get hold of the separate visual tree when a new level is opened. I'm subscribing to the event MenuItem.SubmenuOpenedEvent but this will only give me a reference to the particular MenuItem that has been clicked. I need a reference to the new visual tree root as well. Any suggestions?
Any way of getting hold of new visual trees when they are created anywhere in the application would be great.
I have tried extracting all kinds of private members such as MenuItem._submenuPopup and Application.NonAppWindowsInternal but they all turn out to be null/empty. No luck there.
The SubmenuOpenedEvent is a bit of a misnomer, because at the time it is raised, the submenu hasn't actually been loaded into a visual tree. Thus, it hasn't actually 'opened' in the most meaningful sense of the word.
To be notified when the submenu has been opened, you can subscribe to MenuItem.Loaded. You could do this ahead of time, but I'd probably do it on demand when SubmenuOpenedEvent is raised:
private void OnMenuItemSubmenuOpened(object sender, RoutedEventArgs e)
{
var menuItem = (MenuItem)sender;
var anyChild = menuItem.Items.OfType<FrameworkElement>().FirstOrDefault();
if (anyChild != null)
{
var handler = default(RoutedEventHandler);
handler = (o, args) =>
{
anyChild.Loaded -= handler;
var popup = anyChild?.FindPopup();
// Do something with the Popup.
};
anyChild.Loaded += handler;
}
}
FindPopup is a utility extension method, as defined below:
public static class UIHelpers
{
public static Popup FindPopup(this DependencyObject popupDescendant)
{
if (popupDescendant == null)
return null;
var popupRoot = popupDescendant.FindVisualRoot() ??
popupDescendant;
return popupRoot.GetLogicalParent() as Popup;
}
public static DependencyObject FindVisualRoot(this DependencyObject sourceElement)
{
var currentElement = sourceElement;
while (currentElement != null)
{
var parent = currentElement.GetVisualParent();
if (parent == null)
return currentElement;
currentElement = parent;
}
return null;
}
public static DependencyObject GetVisualParent(this DependencyObject sourceElement)
{
if (sourceElement == null)
return null;
if (sourceElement is Visual || sourceElement is Visual3D)
return VisualTreeHelper.GetParent(sourceElement);
var contentElement = sourceElement as ContentElement;
if (contentElement != null)
return ContentOperations.GetParent(contentElement);
return null;
}
public static DependencyObject GetLogicalParent(this DependencyObject sourceElement)
{
if (sourceElement == null)
return null;
var logicalParent = LogicalTreeHelper.GetParent(sourceElement);
if (logicalParent != null)
return logicalParent;
var frameworkElement = sourceElement as FrameworkElement;
if (frameworkElement != null)
return frameworkElement.TemplatedParent;
return (sourceElement as FrameworkContentElement)?.TemplatedParent;
}
}

Silverlight: How to Make a ShallowCopy of a UIElement

I need to add a UIElement to two different canvases, but one UIElement can only be a child of ONE canvas, so I have to create a ShallowCopy (DeepCopy not needed) of the UIElement.
I want to use MemberwiseClone, but it's protected, I cannot use it.
I also want to define an extension method UIElement.ShallowCopy, but it sill still call MemberwiseClone, which is protected again.
EDIT:
Tried all the following, but all of them failed in Silverlight environment:
// System.Runtime.Serialization.InvalidDataContractException was unhandled by user code
// Message=Type 'System.Windows.UIElement' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required.
public static T CloneEx<T>(this T obj) where T : class
{
T clone;
DataContractSerializer dcs = new DataContractSerializer(typeof(T));
using (MemoryStream ms = new MemoryStream())
{
dcs.WriteObject(ms, obj);
ms.Position = 0;
clone = (T)dcs.ReadObject(ms);
}
return clone;
}
// This one also throws Access/Invoke exceptions
private readonly static object _lock = new object();
public static T MemberwiseCloneEx<T>(this T obj) where T : class
{
if (obj == null)
return null;
try
{
Monitor.Enter(_lock);
T clone = (T)Activator.CreateInstance(obj.GetType());
PropertyInfo[] fields = obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
foreach (PropertyInfo field in fields)
{
object val = field.GetValue(obj, null);
field.SetValue(clone, val, null);
}
return clone;
}
finally
{
Monitor.Exit(_lock);
}
}
// System.MethodAccessException was unhandled by user code
// Message=Attempt by method 'ToonController.ControllerUtils.MemberwiseCloneEx<System.__Canon>(System.__Canon)' to access method 'System.Object.MemberwiseClone()' failed.
public static T MemberwiseCloneEx<T>(this T obj) where T : class
{
if (obj == null)
return null;
MethodInfo mi = obj.GetType().GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic);
if (mi == null)
return null;
return (T)mi.Invoke(obj, null);
}
if you have something that you want to use in multiple ui elements, 'sync them up' then you should create a ViewModel or something similar. This viewmodel would be set to the datacontext of any element you want to use it. Then your shallow reference is simple and you can just create two independent UI elements binding to the same data.

Set hidden AttachedProperty through Style

I've got a problem using System.Windows.Interactivity.Interaction attached behavior class (from Expression Blend SDK 4). I'd like to define a pair of triggers for System.Windows.Window class in XAML Style element. But as the TriggersProperty field of System.Windows.Interactivity.Interaction class is private and there is no SetTriggers method in this class, I've got an error 'Set property System.Windows.Setter.Property threw an exception. -> Value cannot be null. Parameter name: property' when running the following code.
I really want to use the triggers and actions in styles, because I'd like to use them for my window-descendant control. Of course I can use my custom behavior or directly code my window-descendant class with triggers-analogue logic, but I'd like to use already existent triggers and actions of the expression library and my own, not declining them, simply because the TriggersProperty of Interaction class is hidden and I can't set it through style.
Is any workaround for the problem? With Reflection or someway other?
PS. I already tried to declare custom static class with TriggersProperty attached dependency property, registered with the help of AddOwner method, but no help - at the end it still tries to access the same TriggersProperty in the same System.Windows.Interactivity.Interaction class.
<Window
x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:windowsInteractivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<Window.Style>
<Style TargetType="Window">
<Setter Property="Title" Value="WindowStyleTest"/>
<Setter Property="windowsInteractivity:Interaction.Triggers">
<Setter.Value>
<windowsInteractivity:EventTrigger EventName="MouseDown"/>
</Setter.Value>
</Setter>
</Style>
</Window.Style>
</Window>
!!!Update!!!
Okay I took it a bit further. I extended the extension to perform all the work including setting the Triggers collection.
TriggerCollectionExtension
The Extension That does all the heavy lifting.
Note: The first time ProvideValue is called it will be from loading the style so the TargetValue is a Setter.
[ContentProperty("Triggers")]
public class TriggerCollectionExtension : MarkupExtension
{
public string EventName { get; set; }
public string CommandName { get; set; }
public object CommandParameter { get; set; }
public System.Windows.Interactivity.TriggerCollection Triggers { get; private set;}
public TriggerCollectionExtension()
{
var trigCollectionType =
typeof(System.Windows.Interactivity.TriggerCollection);
var triggers = (System.Windows.Interactivity.TriggerCollection)
trigCollectionType.GetConstructor(
BindingFlags. NonPublic | BindingFlags. Instance,
null, Type.EmptyTypes, null).Invoke (null);
// Cheat to get around this problem.
// must have IsFrozen set to false to modify
var methCreateCore = trigCollectionType.GetMethod("CreateInstanceCore",
BindingFlags.NonPublic | BindingFlags.Instance);
var cloneTriggers =
(System.Windows.Interactivity.TriggerCollection)
methCreateCore.Invoke(triggers, null);
this.Triggers = cloneTriggers;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var target = serviceProvider.GetService(typeof(IProvideValueTarget)) as
IProvideValueTarget;
// The first time this is called is when loading the style.
// At that point the TargetObject is of type Setter.
// Return this (The MarkupExtension) and it will be reevaluated when the style
// is applied.
var hostcontrol = target.TargetObject as Control;
if (hostcontrol != null)
{
var cloneTriggers = this.Triggers;
var eventTrigger = new EventTrigger(this.EventName);
var trigbase = eventTrigger as TriggerBase;
trigbase.Attach(hostcontrol);
var commandAction = new CommandAction(hostcontrol, this.CommandName,
this.CommandParameter);
eventTrigger.Actions.Add(commandAction);
cloneTriggers.Add(eventTrigger);
Interaction.SetShadowTriggers(hostcontrol, this.Triggers);
return null;
}
else
{
return this;
}
return null;
}
}
Interaction
The re-ownership/exposure of the TriggersCollection.
<!-- language: c# -->
/// <summary>
/// Helps workaround the bug in the deployed interaction DLL.
/// The DependencyProperty is registered as ShadowTriggers and the Setter Getter is
/// SetTriggers() GetTriggers().
/// The result is compile error for XAML if anything but Shadowtriggers is used and
/// runtime error.
/// </summary>
public static class Interaction
{
static Interaction()
{
var interActionType = typeof(System.Windows.Interactivity.Interaction);
var triggersProperty = (DependencyProperty)interActionType.InvokeMember(
"TriggersProperty",
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.GetField,
null, null, null);
ShadowTriggersProperty = triggersProperty.AddOwner(typeof(Interaction));
}
public static readonly DependencyProperty ShadowTriggersProperty;
public static System.Windows.Interactivity.TriggerCollection
GetShadowTriggers(DependencyObject d)
{
return
(System.Windows.Interactivity.TriggerCollection)
d.GetValue(ShadowTriggersProperty);
}
public static void
SetShadowTriggers(
DependencyObject d,
System.Windows.Interactivity.TriggerCollection value)
{
d.SetValue(ShadowTriggersProperty, value);
}
}
CommandAction
A custom TriggerAction that looks up the Command on the DataContext.
<!-- language: c# -->
public class CommandAction : TriggerAction<FrameworkElement>
{
FrameworkElement control;
private string commandName;
object commandParameter;
private ICommand actualCommand;
public CommandAction(FrameworkElement control, string commandName,
object commandParameter)
{
this.control = control;
this.commandName = commandName;
this.commandParameter = commandParameter;
object datacontext;
if (this.FindDataContext(this.control, out datacontext))
{
var datacontextType = datacontext.GetType();
var propCommand = datacontextType.GetProperty(this.commandName);
this.actualCommand = propCommand.GetValue(datacontext, null) as ICommand;
}
}
private bool FindDataContext(FrameworkElement control, out object datacontext)
{
datacontext = default(object);
var parent = VisualTreeHelper.GetParent(control);
while (parent != null)
{
var parentFrame = parent as FrameworkElement;
if (parentFrame != null)
{
datacontext = parentFrame.DataContext;
if (datacontext != null)
{
return true;
}
}
var parentFrameContent = parent as FrameworkContentElement;
if (parentFrameContent != null)
{
datacontext = parentFrameContent.DataContext;
if (datacontext != null)
{
return true;
}
}
parent = VisualTreeHelper.GetParent(parent);
}
return false;
}
protected override void Invoke(object parameter)
{
if (this.actualCommand != null)
{
if (this.actualCommand.CanExecute(parameter))
{
this.actualCommand.Execute(parameter);
}
}
}
}
Wow long time reader first time posting code. I finally learned why the code doesn't always cut and paste so well. It took so many tries to submit this update.
I am sure there are reasons like disk space, parsing, or rendering speed, and the editor maintains state on failure to submit excellently.
I got it, why the error appears. That's because at runtime it's searching an Attached Dependency property by string name, that is "ShadowTriggers" (as it specified in System.Windows.Interactivity assembly, Interaction static constructor). So I created the own custom static class and inherit the Triggers Dependency Property from System.Windows.Interaction there (via Reflection and AddOwner, just exposed the property as ShadowTriggersProperty). It worked! But... Now I have to provide a TriggerCollection instance to the Style's Property Value Setter, and the constructor of the class is internal. Suppose it is a no way further.

Retrieve all Data Bindings from WPF Window

I have a WPF form which has many controls on it. Many (but not all) of these controls are databound to an underlying object. At certain times, such as when the Save button is pressed, I need to check all the validation rules of my controls. Is there a way to do this programatically, WITHOUT hard-coding a list of the controls to be validated? I want this to continue to work after another developer adds another control and another binding, without having to update some list of bindings to be refreshed.
In a nutshell, is there any way to retrieve the collection of all data bindings from a WPF window?
Try out my sample below. I haven't fully tested this so it may have issues. Also, performance may be questionable. Maybe others can help out to make it faster. But anyway, it seems to do the trick.
Note: A limitation to this, however, is that it may not pick up the bindings defined within Styles or DataTemplates. I'm not sure though. Needs more testing.
Anyway, the solution has three parts basically:
Use VisualTreeHelper to walk the entire visual tree.
For each item in the visual tree, get all dependency properties. Reference.
Use BindingOperations.GetBindingBase to get the binding for each property.
GetBindingsRecursive function:
void GetBindingsRecursive(DependencyObject dObj, List<BindingBase> bindingList)
{
bindingList.AddRange(DependencyObjectHelper.GetBindingObjects(dObj));
int childrenCount = VisualTreeHelper.GetChildrenCount(dObj);
if (childrenCount > 0)
{
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(dObj, i);
GetBindingsRecursive(child, bindingList);
}
}
}
DependencyObjectHelper class:
public static class DependencyObjectHelper
{
public static List<BindingBase> GetBindingObjects(Object element)
{
List<BindingBase> bindings = new List<BindingBase>();
List<DependencyProperty> dpList = new List<DependencyProperty>();
dpList.AddRange(GetDependencyProperties(element));
dpList.AddRange(GetAttachedProperties(element));
foreach (DependencyProperty dp in dpList)
{
BindingBase b = BindingOperations.GetBindingBase(element as DependencyObject, dp);
if (b != null)
{
bindings.Add(b);
}
}
return bindings;
}
public static List<DependencyProperty> GetDependencyProperties(Object element)
{
List<DependencyProperty> properties = new List<DependencyProperty>();
MarkupObject markupObject = MarkupWriter.GetMarkupObjectFor(element);
if (markupObject != null)
{
foreach (MarkupProperty mp in markupObject.Properties)
{
if (mp.DependencyProperty != null)
{
properties.Add(mp.DependencyProperty);
}
}
}
return properties;
}
public static List<DependencyProperty> GetAttachedProperties(Object element)
{
List<DependencyProperty> attachedProperties = new List<DependencyProperty>();
MarkupObject markupObject = MarkupWriter.GetMarkupObjectFor(element);
if (markupObject != null)
{
foreach (MarkupProperty mp in markupObject.Properties)
{
if (mp.IsAttached)
{
attachedProperties.Add(mp.DependencyProperty);
}
}
}
return attachedProperties;
}
}
Sample usage:
List<BindingBase> bindingList = new List<BindingBase>();
GetBindingsRecursive(this, bindingList);
foreach (BindingBase b in bindingList)
{
Console.WriteLine(b.ToString());
}
There is a better solution in .NET 4.5 and above:
foreach (BindingExpressionBase be in BindingOperations.GetSourceUpdatingBindings(element))
{
be.UpdateSource();
}

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