How is it possible to loop through all ContextMenus within a Windows Form?
What I currently have is:
public void SearchContextMenuStrip(Form Window)
{
foreach (Control item in Window.Controls)
{
if (item is ContextMenuStrip)
{
MessageBox.Show("ContextMenuStrip found!", "ContextMenuStrip found");
}
else
{
}
}
}
This works fine for all other controls but not for the ContextMenus.
I want this to be able to translate all text for a multi language application so if someone knows a better solution for this it is much appreciated.
Thanks in advance!
You can associate just one ContextMenuStrip to each control so I think you want ToolStripMenuItem
foreach( var item in Window.ContextMenuStrip.Items)
{
}
Use resx file and for each region-language create it with translation. Use the settings.NAME instead of hard coding. Localization will be applied automatically.
https://msdn.microsoft.com/en-us/library/aa992030(v=vs.100).aspx
I think the best solution is using reflection and finding the ContextMenuStrips between all objects. this script will be able to return all ContextMenuStrips.
public static bool IsLastObject(object obj)
{
if (obj == null) return true;
Type type = obj.GetType();
return
type.IsArray ||
type.IsEnum ||
type.IsPointer ||
type.IsAbstract ||
type.IsSealed;
}
public static IEnumerable<Control> GetAllControls(object obj, int maxNests = 25)
{
if (obj == null || maxNests < 0) yield break;
Type type = obj.GetType();
List<Control> list = new List<Control>();
if (obj is Control)
foreach (Control item in ((Control)obj).Controls)
{
list.Add(item);
yield return item;
}
foreach (var item in type.GetFields())
{
var nobj = item.GetValue(obj);
if (nobj == null) continue;
if (nobj is Control && !list.Contains((Control)nobj)) yield return (Control)nobj;
if (IsLastObject(nobj)) continue;
foreach (var ni in GetAllControls(nobj, maxNests - 1))
yield return ni;
}
}
public static IEnumerable<ContextMenuStrip> GetAllToolStrips(object control, int maxNests = 25)
{
foreach (var item in GetAllControls(control, maxNests))
if (item is ContextMenuStrip) yield return (ContextMenuStrip)item;
}
Enjoy...
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 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 have a groupbox in my application which contains child controls.(As seen in the attchached pic). I want to enumerate through all the textboxes for performing some validation using a simple foreach loop.
This document outline would give a fair idea of the housing of the controls
foreach (Control control in grpBxTargetSensitivity.Controls)
{
if (control is FlowLayoutPanel && control.HasChildren)
{
foreach (Control ctrl in control.Controls)
{
if (ctrl is Panel && ctrl.HasChildren)
{
foreach (Control tbox in ctrl.Controls)
{
if (tbox is TextBox)
{
TextBox textbox = tbox as TextBox;
validData &= !string.IsNullOrWhiteSpace(textbox.Text);
}
}
}
}
}
}
My question is, Is there a better way (possibly through LINQ) to get all the controls including the textboxes housed inside the panels than the above method.?
I don't know that this is any better.. whether it's easier to read is a matter of opinion:
var validData
= grpBxTargetSensitivity.Controls.OfType<FlowLayoutPanel>()
.SelectMany(c => c.Controls.OfType<Panel>())
.SelectMany(c => c.Controls.OfType<TextBox>())
.All(textbox => !string.IsNullOrWhiteSpace(textbox.Text));
This'll grab all TextBoxes inside of all Panels in all FlowLayoutPanels in your GroupBox, and return true if all of those TextBoxes have a value in them.
A one liner slution,
IEnumerable<TextBox> collection = grpBxTargetSensitivity.Children.OfType<TextBox>(); //assuming you are looking for TextBox
or
You can try following generic method,
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;
}
}
}
}
then enumerate over the controls as follows,
foreach (TextBox tb in FindVisualChildren<TextBox>(grpBxTargetSensitivity)) //assuming you are looking for TextBox
{
// do something
}
I create one method by the ability to find any controls (or T type) and any inherited object from Control (or T type) for you:
public static List<T> GetSubControlsOf<T>(Control parent) where T : Control
{
var myCtrls = new List<T>();
foreach (Control ctrl in parent.Controls)
{
// if ctrl is type of T
if (ctrl.GetType() == typeof(T) ||
ctrl.GetType().IsInstanceOfType(typeof(T)))
{
myCtrls.Add(ctrl as T);
}
else if (ctrl.HasChildren)
{
var childs = GetSubControlsOf<T>(ctrl);
if (childs.Any())
myCtrls.AddRange(childs);
}
}
return myCtrls;
}
and use that this form for e.g:
foreach (var textbox in GetSubControlsOf<TextBox>(this))
{
validData &= !string.IsNullOrWhiteSpace(textbox.Text);
}
I found following links on StackOverflow itself:
How to get ALL child controls of a Windows Forms form of a specific type (Button/Textbox)?
Loop through all the user controls on a page
Give them try. Should work in your case.
Im trying to figure out a way to build a somewhat clever version of the jQuery closest method in C#. Im using a generic method to find the desired control and then index the control chain
public static T FindControlRecursive<T>(Control control, string controlID, out List<Control> controlChain) where T : Control
{
controlChain = new List<Control>();
// Find the control.
if (control != null)
{
Control foundControl = control.FindControl(controlID);
if (foundControl != null)
{
// Add the control to the list
controlChain.Add(foundControl);
// Return the Control
return foundControl as T;
}
// Continue the search
foreach (Control c in control.Controls)
{
foundControl = FindControlRecursive<T>(c, controlID);
// Add the control to the list
controlChain.Add(foundControl);
if (foundControl != null)
{
// Return the Control
return foundControl as T;
}
}
}
return null;
}
To Call it
List<Control> controlChain;
var myControl = FindControls.FindControlRecursive<TextBox>(form, "theTextboxId"), out controlChain);
To find the closest element of id or type
// Reverse the list so we search from the "myControl" and "up"
controlChain.Reverse();
// To find by id
var closestById = controlChain.Where(x => x.ID.Equals("desiredControlId")).FirstOrDefault();
// To find by type
var closestByType = controlChain.Where(x => x.GetType().Equals(typeof(RadioButton))).FirstOrDefault();
Would this be a good approach or are there any other cool solutions out there creating this?
Whats your consideration?
Thanks!
Maybe something like this
public static IEnumerable<Control> GetControlHierarchy(Control parent, string controlID)
{
foreach (Control ctrl in parent.Controls)
{
if (ctrl.ID == controlID)
yield return ctrl;
else
{
var result = GetControlHierarchy(ctrl, controlID);
if (result != null)
yield return ctrl;
}
yield return null;
}
}
I am trying to find HtmlForm element using code below and add several new controls. Unfortunately this is not working, the HtmlForm element is never found.
foreach (Control c in Controls)
{
if (c is HtmlForm)
{
c.Controls.AddAt(0, manager);
c.Controls.AddAt(1, updatePanel);
}
}
How about using Page.Form to get the HtmlForm?
Do you have runat="server" specified in the form control? Otherwise; your code behind won't be aware of it.
Also, you might need some recursion here if the Control has nested controls; so something like this:
public static Control FindControlRecursive(Control containerCtl, string controlIdToFind)
{
var foundCtl = containerCtl.FindControl(controlIdToFind);
if (foundCtl != null && controlIdToFind == foundCtl.ID)
{
return foundCtl;
}
foreach (Control ctl in containerCtl.Controls)
{
foundCtl = FindControlRecursive(ctl, controlIdToFind);
if (foundCtl != null && controlIdToFind == foundCtl.ID)
{
return foundCtl;
}
}
return null;
}