I have multiple pictureboxes and I need to load random images into them during runtime. So I thought it would be nice to have a collection of all pictureboxes and then assign images to them using a simple loop. But how should I do it? Or maybe are there any other better solutions to such problem?
Using a bit of LINQ:
foreach(var pb in this.Controls.OfType<PictureBox>())
{
//do stuff
}
However, this will only take care of PictureBoxes in the main container.
You could use this method:
public static IEnumerable<T> GetControlsOfType<T>(Control root)
where T : Control
{
var t = root as T;
if (t != null)
yield return t;
var container = root as ContainerControl;
if (container != null)
foreach (Control c in container.Controls)
foreach (var i in GetControlsOfType<T>(c))
yield return i;
}
Then you could do something like this:
foreach (var pictureBox in GetControlsOfType<PictureBox>(theForm)) {
// ...
}
If you're at least on .NET 3.5 then you have LINQ, which means that since ControlCollection implements IEnumerable you can just do:
var pictureBoxes = Controls.OfType<PictureBox>();
I use this generic recursive method:
The assumption of this method is that if the control is T than the method does not look in its children. If you need also to look to its children you can easily change it accordingly.
public static IList<T> GetAllControlsRecusrvive<T>(Control control) where T :Control
{
var rtn = new List<T>();
foreach (Control item in control.Controls)
{
var ctr = item as T;
if (ctr!=null)
{
rtn.Add(ctr);
}
else
{
rtn.AddRange(GetAllControlsRecusrvive<T>(item));
}
}
return rtn;
}
A simple function, easy to understand, recursive, and it works calling it inside any form control:
private void findControlsOfType(Type type, Control.ControlCollection formControls, ref List<Control> controls)
{
foreach (Control control in formControls)
{
if (control.GetType() == type)
controls.Add(control);
if (control.Controls.Count > 0)
findControlsOfType(type, control.Controls, ref controls);
}
}
You can call it on multiple ways.
To get the Buttons:
List<Control> buttons = new List<Control>();
findControlsOfType(typeof(Button), this.Controls, ref buttons);
To get the Panels:
List<Control> panels = new List<Control>();
findControlsOfType(typeof(Panel), this.Controls, ref panels);
etc.
Here's another version since the existing provided ones weren't quite what I had in mind. This one works as an extension method, optionally, and it excludes checking the root/parent container's type. This method is basically a "Get all descendent controls of type T" method:
public static System.Collections.Generic.IEnumerable<T> ControlsOfType<T>(this System.Web.UI.Control control) where T: System.Web.UI.Control{
foreach(System.Web.UI.Control childControl in control.Controls){
if(childControl is T) yield return (T)childControl;
foreach(var furtherDescendantControl in childControl.ControlsOfType<T>()) yield return furtherDescendantControl;
}
}
public static List<T> FindControlByType<T>(Control mainControl,bool getAllChild = false) where T :Control
{
List<T> lt = new List<T>();
for (int i = 0; i < mainControl.Controls.Count; i++)
{
if (mainControl.Controls[i] is T) lt.Add((T)mainControl.Controls[i]);
if (getAllChild) lt.AddRange(FindControlByType<T>(mainControl.Controls[i], getAllChild));
}
return lt;
}
This to me is by far the easiest. In my application, I was trying to clear all the textboxes in a panel:
foreach (Control c in panel.Controls)
{
if (c.GetType().Name == "TextBox")
{
c.Text = "";
}
}
Takes Control as container into account:
private static IEnumerable<T> GetControlsOfType<T>(this Control root)
where T : Control
{
if (root is T t)
yield return t;
if (root is ContainerControl || root is Control)
{
var container = root as Control;
foreach (Control c in container.Controls)
foreach (var i in GetControlsOfType<T>(c))
yield return i;
}
}
Related
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;
}
}
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;
}
I have Accordion which is bound to ObservableCollection. I need to apply workaround to make Accordion resize its children to the content (ie if an item has been deleted from the bound collection I need the accordion to shrink, and if added - to expand).
However all the workaround I found use AccordionItem objects. They all have AccordionItem items set in XAML so their accordion.Items are collections of AccordionItem objects.
Although I am binding to myObject they are placed in AccordionItem object in the ItemContainerStyleTemplate. The only thing I need is to access that AccordionItem somehow. If I try something like accordion.Items[0].GetType() it returns myObject.
So the question is - how do I access AccordionItem object from data bound Accordion?
The workaround I wanted to try: (EDIT: It does work as I needed)
public static void UpdateSize(this AccordionItem item)
{
item.Dispatcher.BeginInvoke(
delegate
{
if (!item.IsLocked && item.IsSelected)
{
item.IsSelected = false;
item.InvokeOnLayoutUpdated(delegate { item.IsSelected = true; });
}
});
}
I've had to do similar things to Accordions, and the only way I was able to get down to the AccordionItems was by walking the visual tree.
Here's how I did it: Given these extension methods :
public static IEnumerable<DependencyObject> GetAllChildrenOfType(this DependencyObject depObject, Type t, bool recursive = true)
{
List<DependencyObject> objList = new List<DependencyObject>();
var childrenList = depObject.GetChildren();
foreach (DependencyObject i in childrenList)
{
Type ct = i.GetType();
if (ct == t)
objList.Add(i);
if (recursive)
objList.AddRange(i.GetAllChildrenOfType(t));
}
return objList.ToArray();
}
public static IEnumerable<DependencyObject> GetChildren(this DependencyObject depObject)
{
int count = depObject.GetChildrenCount();
for (int i = 0; i < count; i++)
{
yield return VisualTreeHelper.GetChild(depObject, i);
}
}
Now you can get all the AccordionItems in a given Accordion:
var accordionItemList = myAccordion.GetAllChildrenOfType(typeof(AccordionItem));
foreach (AccordionItem i in accordionItemList)
{...}
This may be a bit more complicated than needed, in my instance I had an accordion within the accordion, which made things difficult in the end.
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();
}
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;
}