WPF Nested User Control - wpf

I have build a WPF User Control which contains a ComboBox with a custom popup window which contains a User Control the inner control (the one in the popup) has some properties that I want to expose in the main user control so the host page can read and write to the inner control.
I am having trouble doing this is there something I am doing wrong or is what I am doing ill advised ?
Regards Christian Andersen

you can try exposing it using this
public static IEnumerable<T> FindVisualChildren<T>(this DependencyObject depObj) where T : DependencyObject
{
if (depObj == null) yield break;
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var children = child as T;
if (children != null)
{
yield return children;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
How I use it
var uc = (TabItem)sender;
foreach (TextBlock textBlock in uc.FindVisualChildren<TextBlock>())
{
textBlock.Foreground = Brushes.WhiteSmoke;
}

Related

function that returns a IEnumerable<T> with yield return

I have a function that only returns the first parent item.
public static T FindControlParent<T>(this Control control) where T : Control
{
T parent = default;
if (control.Parent != null)
{
if (control.Parent.GetType() == typeof(T))
{
return (T)control.Parent;
}
return FindControlParent<T>(control.Parent);
}
return parent;
}
I have a button, which is inside two panels, I would like a function that returns a list of the two panels of the button.
something like
Form>panel1>panel2>GroupBox>button
var panelsFinded = FindControlParents<Panel>(button1);
//values: panel1, panel2
I managed to solve it, someone correct me if it can get better
public static IEnumerable<T> FindControlParent2<T>(Control control)
where T : Control
{
if (control != null)
{
Control parent = control.Parent;
if (parent is T)
yield return(T)control.Parent;
foreach (var i in FindControlParent2<T>(parent.Parent))
yield return i;
}
}

Can I create an extension method for a WPF UserControl that can return a list of its controls?

Is this possible while the controls are not publicly exposed through properties ?
this is easy to achive: (code from this SO question)
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
If a control is part of the Visual Tree of that UserControl, then yes, you can list all the visual children of that control. And you can wrap that logic in an extension method.
You can use VisualTreeHelper class for it.

wpf how to get all TextBoxes in wpf application [duplicate]

I'm looking for a way to find all controls on Window by their type,
for example: find all TextBoxes, find all controls implementing specific interface etc.
This should do the trick:
public static IEnumerable<T> FindVisualChilds<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null) yield return (T)Enumerable.Empty<T>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject ithChild = VisualTreeHelper.GetChild(depObj, i);
if (ithChild == null) continue;
if (ithChild is T t) yield return t;
foreach (T childOfChild in FindVisualChilds<T>(ithChild)) yield return childOfChild;
}
}
then you enumerate over the controls like so
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
}
This is the easiest way:
IEnumerable<myType> collection = control.Children.OfType<myType>();
where control is the root element of the window.
EDIT - As pointed out in the comments. This only goes one level deep. See the accepted answer for an option that goes deeper.
I adapted #Bryce Kahle's answer to follow #Mathias Lykkegaard Lorenzen's suggestion and use LogicalTreeHelper.
Seems to work okay. ;)
public static IEnumerable<T> FindLogicalChildren<T> ( DependencyObject depObj ) where T : DependencyObject
{
if( depObj != null )
{
foreach( object rawChild in LogicalTreeHelper.GetChildren( depObj ) )
{
if( rawChild is DependencyObject )
{
DependencyObject child = (DependencyObject)rawChild;
if( child is T )
{
yield return (T)child;
}
foreach( T childOfChild in FindLogicalChildren<T>( child ) )
{
yield return childOfChild;
}
}
}
}
}
(It still won't check tab controls or Grids inside GroupBoxes as mentioned by #Benjamin Berry & #David R respectively.)
(Also followed #noonand's suggestion & removed the redundant child != null)
Use the helper classes VisualTreeHelper or LogicalTreeHelper depending on which tree you're interested in. They both provide methods for getting the children of an element (although the syntax differs a little). I often use these classes for finding the first occurrence of a specific type, but you could easily modify it to find all objects of that type:
public static DependencyObject FindInVisualTreeDown(DependencyObject obj, Type type)
{
if (obj != null)
{
if (obj.GetType() == type)
{
return obj;
}
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject childReturn = FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type);
if (childReturn != null)
{
return childReturn;
}
}
}
return null;
}
I found that the line, VisualTreeHelper.GetChildrenCount(depObj);, used in several examples above does not return a non-zero count for GroupBoxes, in particular, where the GroupBox contains a Grid, and the Grid contains children elements. I believe this may be because the GroupBox is not allowed to contain more than one child, and this is stored in its Content property. There is no GroupBox.Children type of property. I am sure I did not do this very efficiently, but I modified the first "FindVisualChildren" example in this chain as follows:
public IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
int depObjCount = VisualTreeHelper.GetChildrenCount(depObj);
for (int i = 0; i <depObjCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
if (child is GroupBox)
{
GroupBox gb = child as GroupBox;
Object gpchild = gb.Content;
if (gpchild is T)
{
yield return (T)child;
child = gpchild as T;
}
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
Here is yet another, compact version, with the generics syntax:
public static IEnumerable<T> FindLogicalChildren<T>(DependencyObject obj) where T : DependencyObject
{
if (obj != null) {
if (obj is T)
yield return obj as T;
foreach (DependencyObject child in LogicalTreeHelper.GetChildren(obj).OfType<DependencyObject>())
foreach (T c in FindLogicalChildren<T>(child))
yield return c;
}
}
To get a list of all childs of a specific type you can use:
private static IEnumerable<DependencyObject> FindInVisualTreeDown(DependencyObject obj, Type type)
{
if (obj != null)
{
if (obj.GetType() == type)
{
yield return obj;
}
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
foreach (var child in FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type))
{
if (child != null)
{
yield return child;
}
}
}
}
yield break;
}
Small change to the recursion to so you can for example find the child tab control of a tab control.
public static DependencyObject FindInVisualTreeDown(DependencyObject obj, Type type)
{
if (obj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child.GetType() == type)
{
return child;
}
DependencyObject childReturn = FindInVisualTreeDown(child, type);
if (childReturn != null)
{
return childReturn;
}
}
}
return null;
}
And this is how it works upwards
private T FindParent<T>(DependencyObject item, Type StopAt) where T : class
{
if (item is T)
{
return item as T;
}
else
{
DependencyObject _parent = VisualTreeHelper.GetParent(item);
if (_parent == null)
{
return default(T);
}
else
{
Type _type = _parent.GetType();
if (StopAt != null)
{
if ((_type.IsSubclassOf(StopAt) == true) || (_type == StopAt))
{
return null;
}
}
if ((_type.IsSubclassOf(typeof(T)) == true) || (_type == typeof(T)))
{
return _parent as T;
}
else
{
return FindParent<T>(_parent, StopAt);
}
}
}
}
Do note that using the VisualTreeHelper does only work on controls that derive from Visual or Visual3D. If you also need to inspect other elements (e.g. TextBlock, FlowDocument etc.), using VisualTreeHelper will throw an exception.
Here's an alternative that falls back to the logical tree if necessary:
http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways
My version for C++/CLI
template < class T, class U >
bool Isinst(U u)
{
return dynamic_cast< T >(u) != nullptr;
}
template <typename T>
T FindVisualChildByType(Windows::UI::Xaml::DependencyObject^ element, Platform::String^ name)
{
if (Isinst<T>(element) && dynamic_cast<Windows::UI::Xaml::FrameworkElement^>(element)->Name == name)
{
return dynamic_cast<T>(element);
}
int childcount = Windows::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(element);
for (int i = 0; i < childcount; ++i)
{
auto childElement = FindVisualChildByType<T>(Windows::UI::Xaml::Media::VisualTreeHelper::GetChild(element, i), name);
if (childElement != nullptr)
{
return childElement;
}
}
return nullptr;
};
#Bryce, really nice answer.
VB.NET version:
Public Shared Iterator Function FindVisualChildren(Of T As DependencyObject)(depObj As DependencyObject) As IEnumerable(Of T)
If depObj IsNot Nothing Then
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(depObj) - 1
Dim child As DependencyObject = VisualTreeHelper.GetChild(depObj, i)
If child IsNot Nothing AndAlso TypeOf child Is T Then
Yield DirectCast(child, T)
End If
For Each childOfChild As T In FindVisualChildren(Of T)(child)
Yield childOfChild
Next
Next
End If
End Function
Usage (this disables all TextBoxes in a window):
For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me)
tb.IsEnabled = False
Next
For this and more use cases you can add flowing extension method to your library:
public static List<DependencyObject> FindAllChildren(this DependencyObject dpo, Predicate<DependencyObject> predicate)
{
var results = new List<DependencyObject>();
if (predicate == null)
return results;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dpo); i++)
{
var child = VisualTreeHelper.GetChild(dpo, i);
if (predicate(child))
results.Add(child);
var subChildren = child.FindAllChildren(predicate);
results.AddRange(subChildren);
}
return results;
}
Example for your case:
var children = dpObject.FindAllChildren(child => child is TextBox);
I wanted to add a comment but I have less than 50 pts so I can only "Answer".
Be aware that if you use the "VisualTreeHelper" method to retrieve XAML "TextBlock" objects then it will also grab XAML "Button" objects. If you re-initialize the "TextBlock" object by writing to the Textblock.Text parameter then you will no longer be able to change the Button text using the Button.Content parameter. The Button will permanently show the text written to it from the Textblock.Text write action (from when it was retrieved --
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
tb.Text = ""; //this will overwrite Button.Content and render the
//Button.Content{set} permanently disabled.
}
To work around this, you can try using a XAML "TextBox" and add methods (or Events) to mimic a XAMAL Button. XAML "TextBox" is not gathered by a search for "TextBlock".
For some reason, none of the answers posted here helped me to get all controls of given type contained in a given control in my MainWindow.
I needed to find all menu items in one menu to iterate them. They were not all direct descendants of the menu, so i managed to collect only the first lilne of them using any of the code above.
This extension method is my solution for the problem for anyone who will continue to read all the way down here.
public static void FindVisualChildren<T>(this ICollection<T> children, DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
var brethren = LogicalTreeHelper.GetChildren(depObj);
var brethrenOfType = LogicalTreeHelper.GetChildren(depObj).OfType<T>();
foreach (var childOfType in brethrenOfType)
{
children.Add(childOfType);
}
foreach (var rawChild in brethren)
{
if (rawChild is DependencyObject)
{
var child = rawChild as DependencyObject;
FindVisualChildren<T>(children, child);
}
}
}
}
Hope it helps.
I found it easier without Visual Tree Helpers:
foreach (UIElement element in MainWindow.Children) {
if (element is TextBox) {
if ((element as TextBox).Text != "")
{
//Do something
}
}
};
The accepted answer returns the discovered elements more or less unordered, by following the first child branch as deep as possible, while yielding the discovered elements along the way, before backtracking and repeating the steps for not yet parsed tree branches.
If you need the descendent elements in descending order, where the direct children will be yielded first, then their children and so on, the following algorithm will work:
public static IEnumerable<T> GetVisualDescendants<T>(DependencyObject parent, bool applyTemplates = false)
where T : DependencyObject
{
if (parent == null || !(child is Visual || child is Visual3D))
yield break;
var descendants = new Queue<DependencyObject>();
descendants.Enqueue(parent);
while (descendants.Count > 0)
{
var currentDescendant = descendants.Dequeue();
if (applyTemplates)
(currentDescendant as FrameworkElement)?.ApplyTemplate();
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(currentDescendant); i++)
{
var child = VisualTreeHelper.GetChild(currentDescendant, i);
if (child is Visual || child is Visual3D)
descendants.Enqueue(child);
if (child is T foundObject)
yield return foundObject;
}
}
}
The resulting elements will be ordered from nearest to farthest.
This will be useful e.g. if you are looking for the nearest child element of some type and condition:
var foundElement = GetDescendants<StackPanel>(someElement)
.FirstOrDefault(o => o.SomeProperty == SomeState);

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();
}

Generic method to find all TextBox controls in Silverlight

I have several Silverlight controls on a page and want query all the controls that are of type TextBox and have that working.
Now the Silverlight form I'm working on could have more TextBox controls added. So when I test to see if a TextBox control has a value, I could do:
if (this.TextBox.Control.value.Text() != String.Empty)
{
// do whatever
}
but I'd rather have if flexible that I can use this on ANY Silverlight form regardless of the number of TextBox controls I have.
Any ideas on how I would go about doing that?
I have already faced this issue and notify it here : http://megasnippets.com/en/source-codes/silverlight/Get_all_child_controls_recursively_in_Silverlight
Here you have a generic method to find recursively in the VisualTree all TextBoxes:
IEnumerable<DependencyObject> GetChildrenRecursively(DependencyObject root)
{
List<DependencyObject> children = new List<DependencyObject>();
children.Add(root);
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++)
children.AddRange(GetChildrenRecursively(VisualTreeHelper.GetChild(root, i)));
return children;
}
Use this method like this to find all TextBoxes:
var textBoxes = GetChildrenRecursively(LayoutRoot).OfType<TextBox>();
It sounds like you need a recursive routine like GetTextBoxes below:
void Page_Loaded(object sender, RoutedEventArgs e)
{
// Instantiate a list of TextBoxes
List<TextBox> textBoxList = new List<TextBox>();
// Call GetTextBoxes function, passing in the root element,
// and the empty list of textboxes (LayoutRoot in this example)
GetTextBoxes(this.LayoutRoot, textBoxList);
// Now textBoxList contains a list of all the text boxes on your page.
// Find all the non empty textboxes, and put them into a list.
var nonEmptyTextBoxList = textBoxList.Where(txt => txt.Text != string.Empty).ToList();
// Do something with each non empty textbox.
nonEmptyTextBoxList.ForEach(txt => Debug.WriteLine(txt.Text));
}
private void GetTextBoxes(UIElement uiElement, List<TextBox> textBoxList)
{
TextBox textBox = uiElement as TextBox;
if (textBox != null)
{
// If the UIElement is a Textbox, add it to the list.
textBoxList.Add(textBox);
}
else
{
Panel panel = uiElement as Panel;
if (panel != null)
{
// If the UIElement is a panel, then loop through it's children
foreach (UIElement child in panel.Children)
{
GetTextBoxes(child, textBoxList);
}
}
}
}
Instantiate an empty list of TextBoxes. Call GetTextBoxes, passing in the root control on your page (in my case, that's this.LayoutRoot), and GetTextBoxes should recursively loop through every UI element that is a descendant of that control, testing to see if it's either a TextBox (add it to the list), or a panel, that might have descendants of it's own to recurse through.
Hope that helps. :)
From your top most panel you can do this (my grid is called ContentGrid)
var textBoxes = this.ContentGrid.Children.OfType<TextBox>();
var nonEmptyTextboxes = textBoxes.Where(t => !String.IsNullOrEmpty(t.Text));
foreach (var textBox in nonEmptyTextboxes)
{
//Do Something
}
However this will only find the textboxes that are immediate children. Some sort of recursion like below would help, but I'm thinking there must be a better way.
private List<TextBox> SearchForTextBoxes(Panel panel)
{
List<TextBox> list = new List<TextBox>();
list.AddRange(panel.Children.OfType<TextBox>()
.Where(t => !String.IsNullOrEmpty(t.Text)));
var panels = panel.Children.OfType<Panel>();
foreach (var childPanel in panels)
{
list.AddRange(SearchForTextBoxes(childPanel));
}
return list;
}
Took Scott's initial idea and expanded it so that it
Uses generics, so it easily copes with multiple control types.
Supports more container types. In my WP7 I needed to support panaorama's, scroll viewers etc... which aren't Panels. So this allows support for them.
Biggest issue is that string comparing, especially on the Panel and derrived items.
Code:
private static void GetControls<T>(UIElement uiElement, List<T> controlList) where T : UIElement
{
var frameworkFullName = uiElement.GetType().FullName;
if (frameworkFullName == typeof(T).FullName)
{
controlList.Add(uiElement as T);
return;
}
if (frameworkFullName == typeof(Panel).FullName ||
frameworkFullName == typeof(Grid).FullName ||
frameworkFullName == typeof(StackPanel).FullName)
{
foreach (var child in (uiElement as Panel).Children)
{
GetControls(child, controlList);
}
return;
}
if (frameworkFullName == typeof(Panorama).FullName)
{
foreach (PanoramaItem child in (uiElement as Panorama).Items)
{
var contentElement = child.Content as FrameworkElement;
if (contentElement != null)
{
GetControls(contentElement, controlList);
}
}
return;
}
if (frameworkFullName == typeof(ScrollViewer).FullName)
{
var contentElement = (uiElement as ScrollViewer).Content as FrameworkElement;
if (contentElement != null)
{
GetControls(contentElement, controlList);
}
return;
}
}
Similar logic to ideas above to also handle controls with a "Content" attribute like TabItems and Scrollviewers where children might be embedded at a lower level. Finds all children:
IEnumerable<DependencyObject> GetControlsRecursive(DependencyObject root)
{
List<DependencyObject> elts = new List<DependencyObject>();
elts.Add(root);
string type = root.GetType().ToString().Replace("System.Windows.Controls.", "");
switch (root.GetType().ToString().Replace("System.Windows.Controls.", ""))
{
case "TabItem":
var TabItem = (TabItem)root;
elts.AddRange(GetControlsRecursive((DependencyObject)TabItem.Content));
break;
case "ScrollViewer":
var Scroll = (ScrollViewer)root;
elts.AddRange(GetControlsRecursive((DependencyObject) Scroll.Content));
break;
default: //controls that have visual children go here
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++) elts.AddRange(GetControlsRecursive(VisualTreeHelper.GetChild(root, i)));
break;
}
return elts;
}

Resources