How do I loop through the all controls in a window in WPF?
I found this in the MSDN documenation so it helps.
// Enumerate all the descendants of the visual object.
static public void EnumVisual(Visual myVisual)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(myVisual); i++)
{
// Retrieve child visual at specified index value.
Visual childVisual = (Visual)VisualTreeHelper.GetChild(myVisual, i);
// Do processing of the child visual object.
// Enumerate children of the child visual object.
EnumVisual(childVisual);
}
}
Looks simpler to me. I used it to find textboxes in a form and clear their data.
This way is superior to the MSDN method, in that it's reusable, and it allows early aborting of the loop (i.e. via, break;, etc.) -- it optimizes the for loop in that it saves a method call for each iteration -- and it also lets you use regular for loops to loop through a Visual's children, or even recurse it's children and it's grand children -- so it's much simpler to consume.
To consume it, you can just write a regular foreach loop (or even use LINQ):
foreach (var ctrl in myWindow.GetChildren())
{
// Process children here!
}
Or if you don't want to recurse:
foreach (var ctrl in myWindow.GetChildren(false))
{
// Process children here!
}
To make it work, you just need put this extension method into any static class, and then you'll be able to write code like the above anytime you like:
public static IEnumerable<Visual> GetChildren(this Visual parent, bool recurse = true)
{
if (parent != null)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
// Retrieve child visual at specified index value.
var child = VisualTreeHelper.GetChild(parent, i) as Visual;
if (child != null)
{
yield return child;
if (recurse)
{
foreach (var grandChild in child.GetChildren(true))
{
yield return grandChild;
}
}
}
}
}
}
Also, if you don't like recursion being on by default, you can change the extension method's declaration to have recurse = false be the default behavior.
Class to get a list of all the children's components of a control:
class Utility
{
private static StringBuilder sbListControls;
public static StringBuilder GetVisualTreeInfo(Visual element)
{
if (element == null)
{
throw new ArgumentNullException(String.Format("Element {0} is null !", element.ToString()));
}
sbListControls = new StringBuilder();
GetControlsList(element, 0);
return sbListControls;
}
private static void GetControlsList(Visual control, int level)
{
const int indent = 4;
int ChildNumber = VisualTreeHelper.GetChildrenCount(control);
for (int i = 0; i <= ChildNumber - 1; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(control, i);
sbListControls.Append(new string(' ', level * indent));
sbListControls.Append(v.GetType());
sbListControls.Append(Environment.NewLine);
if (VisualTreeHelper.GetChildrenCount(v) > 0)
{
GetControlsList(v, level + 1);
}
}
}
}
I've used the following to get all controls.
public static IList<Control> GetControls(this DependencyObject parent)
{
var result = new List<Control>();
for (int x = 0; x < VisualTreeHelper.GetChildrenCount(parent); x++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, x);
var instance = child as Control;
if (null != instance)
result.Add(instance);
result.AddRange(child.GetControls());
}
return result;
}
A slight variation on the MSDN answer ... just pass in an empty List of Visual objects into it and your collection will be populated with all the child visuals:
/// <summary>
/// Enumerate all the descendants (children) of a visual object.
/// </summary>
/// <param name="parent">Starting visual (parent).</param>
/// <param name="collection">Collection, into which is placed all of the descendant visuals.</param>
public static void EnumVisual(Visual parent, List<Visual> collection)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
// Get the child visual at specified index value.
Visual childVisual = (Visual)VisualTreeHelper.GetChild(parent, i);
// Add the child visual object to the collection.
collection.Add(childVisual);
// Recursively enumerate children of the child visual object.
EnumVisual(childVisual, collection);
}
}
The previous answers will all return the children that are identified by VisualTreeHelper.GetChildrenCount and VisualTreeHelper.GetChild. However, I have found that for a TabControl, the TabItems and their content are not identified as children. Thus, these would be omitted, and I think the original question ("all controls in a window") would like to have them included.
To properly loop through tabbed controls as well, you will need something like this (modified from the answer of #BrainSlugs83):
public static IEnumerable<Visual> GetChildren(this Visual parent, bool recurse = true)
{
if (parent != null)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
// Retrieve child visual at specified index value.
var child = VisualTreeHelper.GetChild(parent, i) as Visual;
if (child != null)
{
yield return child;
if (recurse)
{
foreach (var grandChild in child.GetChildren(true))
{
yield return grandChild;
}
// Tabs and their content are not picked up as visual children
if (child is TabControl childTab)
{
foreach (var childTabItem in childTab.Items)
{
yield return childTabItem;
foreach (var childTabItemChild in childTabItem.GetChildren(true))
{
yield return childTabItemChild;
}
if (childTabItem.Content != null && childTabItem.Content is Visual childTabItemContentAsVisual)
{
yield return childTabItemContentAsVisual;
foreach (var childTabItemGrandChild in childTabItemContentAsVisual.Children(true)
{
yield return childTabItemGrandChild;
}
}
}
}
}
}
}
}
}
Alternatively, you could iterate over the logical tree instead of the visual tree:
public static IEnumerable<DependencyObject> GetLogicalChildren(this DependencyObject parent, bool recurse = true)
{
if (parent == null) yield break;
foreach (var child in LogicalTreeHelper.GetChildren(parent).OfType<DependencyObject>())
{
yield return child;
if (recurse)
{
foreach (var grandChild in child.GetLogicalChildren(true))
{
yield return grandChild;
}
}
}
}
I tried it myself and I found an elegant solution that also works in any scenario, not like all the solutions posted here that are overengineered and broken. The majority of the answers here are over-engineered and unstable.
The idea is to loop through a parent control in Windows Presentation Foundation to get the children controls of the desired type or types.
First you need a loop that has an integer, set to zero preferably, with the use of indexing and a VisualTreeHelper object that is counting all the objects within the parent control as the loop's condition.
for(int ControlCounter = 0; ControlCounter <= VisualTreeHelper.GetChildrenCount(MaterialsContentComputerSystemsFoundationYear) - 1; ControlCounter++)
{
if(VisualTreeHelper.GetChild(MaterialsContentComputerSystemsFoundationYear, ControlCounter).GetType() == File1.GetType())
{
Button b = (Button)VisualTreeHelper.GetChild(MaterialsContentComputerSystemsFoundationYear, ControlCounter);
if (ActualButtonControlIndex == App.FileIndex[ActualButtonControlIndex])
{
}
else
{
b.Visibility = Visibility.Hidden;
}
ActualButtonControlIndex++;
System.Diagnostics.Debug.WriteLine(ActualButtonControlIndex + " Button");
}
}
Within the for loop you can make a conditional statement that is verifying if the type of the current control at the current index is equal with the type of control that is desired. In this example I used a control that is named and is part of the desired type of control that is currently searched. You can use a variable that is storing a button instead, for type comparison.
var b = new Button();
for(int ControlCounter = 0; ControlCounter <= VisualTreeHelper.GetChildrenCount(MaterialsContentComputerSystemsFoundationYear) - 1; ControlCounter++)
{
if(VisualTreeHelper.GetChild(MaterialsContentComputerSystemsFoundationYear, ControlCounter).GetType() == b.GetType())
{
Button B = (Button)VisualTreeHelper.GetChild(MaterialsContentComputerSystemsFoundationYear, ControlCounter);
if (ActualButtonControlIndex == App.FileIndex[ActualButtonControlIndex])
{
}
else
{
B.Visibility = Visibility.Hidden;
}
ActualButtonControlIndex++;
System.Diagnostics.Debug.WriteLine(ActualButtonControlIndex + " Button");
}
}
Within the for loop's conditional statement, an object of the type of the desired control is created that has its value set with the value of the VisualTreeHelper object at the current index casted to the type Button.
You can use the previously mentioned button to set proprieties like size, with and content, colour, etc. to the control in the application's window, within the parent control within that window, at the current index.
var b = new Button();
for(int ControlCounter = 0; ControlCounter <= VisualTreeHelper.GetChildrenCount(MaterialsContentComputerSystemsFoundationYear) - 1; ControlCounter++)
{
if(VisualTreeHelper.GetChild(MaterialsContentComputerSystemsFoundationYear, ControlCounter).GetType() == b.GetType())
{
Button B = (Button)VisualTreeHelper.GetChild(MaterialsContentComputerSystemsFoundationYear, ControlCounter);
if (ActualButtonControlIndex == App.FileIndex[ActualButtonControlIndex])
{
B.Content = "Hello";
B.FontSize = 20;
B.BackGround = new SolidColorBrush(Colors.Red);
}
else
{
B.Visibility = Visibility.Hidden;
}
ActualButtonControlIndex++;
System.Diagnostics.Debug.WriteLine(ActualButtonControlIndex + " Button");
}
}
This solution is modular, simple and super-stable and thus useful in any scenario, give a like.
Related
I want to check controls if they are empty and make exception for some Controls for example i want to make exception for these Textboxes Notes_txtbox and Job_txt
. This is my function but it doesn't give me correct result . It gives Notes_txtbox,Job_txt are empty :
public bool Check_Inputs_Empty_Exception(List<Control> Exception_Control_List,
TableLayoutPanel TableLayoutPanel)
{
bool is_empy = false;
foreach (Control Control in TableLayoutPanel.Controls)
{
for (int i = 0; i < Exception_Control_List.Count; i++)
{
if (Control.Name != Exception_Control_List[i].Name &&
Control.GetType() == Exception_Control_List[i].GetType())
{
if (String.IsNullOrWhiteSpace(Control.Text))
{
MessageBox.Show(Control.Name + " is empty");
is_empy = true;
}
}
}
}
return is_empy;
}
}
And this is Function Call :
if (Check_Inputs_Empty_Exception(
new List<Control> {Notes_txtbox,Job_txt}, TableLayoutPanel) == false)
{
// My Add Function
}
This is a simple and common logic flaw. Your loops are set up with the wrong logic gates. You are simply searching for any non-match, which you will always find (for every control in the control list there is a control in the exception list that is not the same control, unless the exception list has exactly one element).
Instead, search for any single match and set a flag if found. When the flag is set you move the outer loop on to the next iteration.
public bool Check_Inputs_Empty_Exception(List<Control> Exception_Control_List, TableLayoutPanel TableLayoutPanel)
{
bool is_empy = false;
foreach (Control Control in TableLayoutPanel.Controls)
{
bool found = false;
for (int i = 0; i < Exception_Control_List.Count; i++)
{
if (Control == Exception_Control_List[i])
{
found = true;
break;
}
}
if (found) continue;
if (String.IsNullOrWhiteSpace(Control.Text))
{
MessageBox.Show(Control.Name + " is empty");
is_empy = true;
}
}
return is_empy;
}
With a little LINQ you can simplify this quite a bit too. Also, if you just want textboxes, you can filter out everything else using OfType<>().
public bool Check_Inputs_Empty_Exception(List<Control> Exception_Control_List, TableLayoutPanel TableLayoutPanel)
{
bool result = false;
var emptyTextboxes = TableLayoutPanel.Controls
.OfType<TextBox>()
.Where( c => string.IsNullOrEmpty(c.Text) )
.ToList();
foreach (var control in emptyTextboxes)
{
if (!Exception_Control_List.Any( c => c == control ))
{
MessageBox.Show(control.Name + " is empty");
result = true;
}
}
return result;
}
I suggest using Linq instead of for and query the controls
// signature: more natural have "parent, excluded":
// "scan parent without excluded"
// IEnumerable<Control> - now I can pass almost any collection, say array
public static bool Check_Inputs_Empty_Exception<T>(Control parent,
IEnumerable<T> excluded)
where T : Control {
if (null == parent)
throw new ArgumentNullException(nameof(parent));
// HashSet is more efficient (and convenient) for Contains then List
HashSet<T> exceptions = excluded == null
? new HashSet<T>()
: new HashSet<T>(excluded);
// array of Controls of type T on parent with empty Text and not within exceptions
var empty = parent
.Controls
.OfType<T>()
.Where(control => !exceptions.Contains(control))
.Where(control => string.IsNullOrEmpty(control.Text))
// .OrderBy(control => control.Name) //TODO: you may want to sort the controls
.ToArray();
foreach (T ctrl in empty)
MessageBox.Show($"{ctrl.Name} is empty");
return empty.Any();
}
// If we don't have controls to exclude
public static bool Check_Inputs_Empty_Exception<T>(Control parent)
where T : Control {
return Check_Inputs_Empty_Exception<T>(parent, new T[0]);
}
usage
if (!Check_Inputs_Empty_Exception<TextBox>(TableLayoutPanel,
new TextBox[] {Notes_txtbox, Job_txt})) {
//TODO: relevant code here
}
Edit: If you want to test, say, TextBoxs only (exclude all other Controls like Panels, Buttons) you can use generics
I'm trying to work out a way to iterate through all the children (including children of children) of an object without creating garbage allocation.
The function I had before was a Recursive function that returned a list.
Now I have two functions one that returns a count and the other that gets the child at a certain index.
I feel like there might be a better way to iterate through all the children of an object
Here is a sample code comparing the recursive list and the recursive count + getAt(index)
public class MyClassWithChildren
{
private MyClassWithChildren[] m_Children;
//With allocation
public IReadOnlyList<MyClassWithChildren> GetAllChildren(bool includeThis = false)
{
var allChildren = new List<MyClassWithChildren>();
if (includeThis) {
allChildren.Add(this);
}
if (m_Children != null) {
for (int i = 0; i < m_Children.Length; i++) {
allChildren.AddRange(m_Children[i].GetAllChildren(true));
}
}
return allChildren;
}
//Without allocation combination of count and getAt(index)
public int GetAllChildrenCount(bool includeThis = false)
{
var count = 0;
if (includeThis) { count++; }
for (int i = 0; i < m_Children.Length; i++) {
count += 1 + m_Children[i].GetAllChildrenCount();
}
return count;
}
public MyClassWithChildren GetAllChildrenAt(int index, bool includeThis = false)
{
if (includeThis) {
if (index == 0) { return this;}
index--;
}
for (int i = 0; i < m_Children.Length; i++) {
if (index == 0) { return m_Children[i]; }
index--;
var newIndex = index - m_Children[i].GetAllChildrenCount(false);
if (newIndex < 0) { return m_Children[i].GetAllChildrenAt(index); }
index = newIndex;
}
return null;
}
}
Does anyone know of a better way to do this?
A simple use case for this would be to search if a certain object is a child of another.
Another would be to find all the children that have a certain property value.
Thank you for your time
Maybe this will is what you're looking for:
public IEnumerable<MyClassWithChildren> GetAllChildren()
{
var items = new Queue<MyClassWithChildren>();
items.Enqueue(this);
while (items.TryDequeue(out var result))
{
yield return result;
for (var i = 0; i < result.m_children.Length; ++i) // use for instead of foreach to avoid enumerator creation
{
items.Enqueue(result.m_children[i]);
}
}
}
This will evaluate the children of a returned value after the external loop has processed it and requests the next one. That means all children are lazy iterated. If your external loop will stop after the first element only this element has been enqueued to the result queue. There is no overhead for enumerators in m_children because this code uses a for-loop instead of a foreach-loop.
If you need the count of all Elements - just use linq: GetAllChildren().Count().
I have a wpf application and I want to set everything to Focusable="false".
Is there an easy and elegant way? Currently I made a style for each type of Control I use like this:
<Style TargetType="Button">
<Setter Property="Focusable" Value="False"></Setter>
</Style>
Any idea for a more universan solution?
Why not try a two line solution ?
foreach (var ctrl in myWindow.GetChildren())
{
//Add codes here :)
}
Also make sure to add this :
public static IEnumerable<Visual> GetChildren(this Visual parent, bool recurse = true)
{
if (parent != null)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
// Retrieve child visual at specified index value.
var child = VisualTreeHelper.GetChild(parent, i) as Visual;
if (child != null)
{
yield return child;
if (recurse)
{
foreach (var grandChild in child.GetChildren(true))
{
yield return grandChild;
}
}
}
}
}
}
Or even shorter, use this :
public static IList<Control> GetControls(this DependencyObject parent)
{
var result = new List<Control>();
for (int x = 0; x < VisualTreeHelper.GetChildrenCount(parent); x++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, x);
var instance = child as Control;
if (null != instance)
result.Add(instance);
result.AddRange(child.GetControls());
}
return result;
}
I have some few cases in my WPF application that requires me to find a specific type of user control in a given user control. For example I'm having the following method that already works nicely:
public static System.Windows.Controls.CheckBox FindChildCheckBox(DependencyObject d)
{
try
{
System.Windows.Controls.CheckBox chkBox = d as System.Windows.Controls.CheckBox;
if (d != null && chkBox == null)
{
int count = System.Windows.Media.VisualTreeHelper.GetChildrenCount(d);
for (int i = 0; i < count; i++)
{
chkBox = FindChildCheckBox(System.Windows.Media.VisualTreeHelper.GetChild(d, i));
if (chkBox != null)
break;
}
}
return chkBox;
}
catch
{
return null;
}
}
This method will help me to find a CheckBox in a given ListViewItem which allows me to check/uncheck the said CheckBox more conveniently.
However, I'd like to have this method more generic like for example:
public static T FindChildUserControl<T>(DependencyObject d)
Unfortunately I do not see how I can get this work. Can someone please help?
You need to replace CheckBox with T and add a generic restraint (where) to the type argument.
For example I'm having the following method that already works nicely
Which is odd, as far as I can tell it would only work on nested CheckBoxes. This should on any combination of controls:
public static T FindChild<T>(DependencyObject d) where T : DependencyObject
{
if (d is T)
return (T)d;
int count = System.Windows.Media.VisualTreeHelper.GetChildrenCount(d);
for (int i = 0; i < count; i++)
{
DependencyObject child = FindChild<T>(System.Windows.Media.VisualTreeHelper.GetChild(d, i));
if (child != null)
return (T)child;
}
return null;
}
Usage:
CheckBox check = FindChild<CheckBox>(parent);
To get all children of a certain type, this should work nicely:
public static IEnumerable<T> FindChildren<T>(DependencyObject d) where T : DependencyObject
{
if (d is T)
yield return (T)d;
int count = System.Windows.Media.VisualTreeHelper.GetChildrenCount(d);
for (int i = 0; i < count; i++)
{
foreach (T child in FindChildren<T>(System.Windows.Media.VisualTreeHelper.GetChild(d, i)))
yield return child;
}
}
Usage:
foreach(CheckBox c in FindChildren<CheckBox>(parent))
This method will help me to find a CheckBox in a given ListViewItem which allows me to check/uncheck the said CheckBox more conveniently.
You should use MVVM instead. Walking down the VisualTree is a really hacky workaround.
I'm writing custom control where I switch default ItemsPanel with
ItemsPanelProperty.OverrideMetadata(typeof(DataGrid),
new FrameworkPropertyMetadata(new ItemsPanelTemplate(
new FrameworkElementFactory(typeof(AdvancedVirtualizingStackPanel)))));
How can I get reference to the instance of created AdvancedVirtualizingStackPanel?
Ok, I will answer myself.
First you need to get ItemsPresenter. You can use this code:
ItemsPresenter itemsPresenter =
(ItemsPresenter)GetTemplateChild(ItemsPresenterTemplateName);
// You have to name ItemsPresenter (using value of
// ItemsPresenterTemplateName string) in your XAML
Then:
itemsPresenter.ApplyTemplate(); // In case template is not applied yet
_stackPanel =
Helper.GetVisualChild<AdvancedVirtualizingStackPanel>(itemsPresenter);
And you got your panel.
Code for Helper.GetVisualChild():
public static 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;
}