Equivalent to jQuery closest() in ASP.NET Web Forms - c#

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

Related

Check All Controls If Empty And Make Exception For Some Controls

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

Find all ContextMenuStrips in Windows Form

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...

Get the list of Child controls inside a groupbox

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.

How to find HtmlForm element?

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

jscript document.getElementsByName in C#

which is similar in c# ?
var e = document.getElementsByName("test")[0];
var HTML = e.innerHTML;
Recursive FindControl method:
private Control RecursiveControlFind(Control parent, string controlID)
{
Control child = parent.FindControl(controlID);
if (child == null)
{
if (parent.Controls.Count > 0)
{
foreach (Control nestedControl in parent.Controls)
{
child = RecursiveControlFind(nestedControl, controlID);
if (child != null)
break;
}
}
}
return child;
}
Control.FindControl Method but it isn't recursive.

Categories

Resources