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
Related
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.
Hello all I am slowly learning c#, and I have this problem:
I want to make a function that sets all panels not visible and then set one as visible given to me from a string variable.
public void setMeVisible(string PanelName)
{
PageMainScreen.Visible = false;
PageNewRegistration.Visible = false;
PageSelectedPatient.Visible = false;
["PanelName"].Visible = true; // in this line I want to set the value of
// PanelName.Visible=true.
// what is the format that I must put PanelName?
}
For Windows, The simplest way is loop through all the controls and check if its a panel and set it to false. In the same loop you can check panel.Name for the pass in "PanelName" and set it to true. Something like this
foreach(Control control in form.Controls)
{
if( control is Panel )
{
control.Visible = false;
if(control.Name == "PanelName")
control.Visible = true;
}
}
Use Page.FindControl for ASP.Net:
var control = FindControl(PanelName);
if (control != null)
control.Visible = true;
If your control is nested, you can create a FindControlRecursive function, suggested by this answer:
private Control FindControlRecursive(Control root, string id)
{
return root.ID == id
? root
: (root.Controls.Cast<Control>()
.Select(c => FindControlRecursive(c, id)))
.FirstOrDefault(t => t != null);
}
Then call it by doing:
var control = FindControlRecursive(form1, PanelName); // Or another top-level control other than form1
if (control != null)
control.Visible = true;
Use Controls.Find for a windows form application:
var control = Controls.Find(PanelName, true).FirstOrDefault();
if (control != null)
control.Visible = true;
Looping and finding controls is not really necessary. You should add all your panels to a same container, use the Controls collection and pass in the name to get the control:
public void setMeVisible(string PanelName) {
PageMainScreen.Visible = false;
PageNewRegistration.Visible = false;
PageSelectedPatient.Visible = false;
Control c = sameContainer.Controls[PanelName];
if(c != null) c.Visible = true;
}
If each time there is only 1 panel visible, you should use some variable to track the current shown panel and hide only this (instead of all controls as you did) like this:
Control currentShown;
public void setMeVisible(string PanelName) {
Control c = sameContainer.Controls[PanelName];
if(c != null) {
c.Visible = true;
if(currentShown != null) currentShown.Visible = false;
currentShown = c;
}
}
And the last, if you don't want to use the same container for all your panels. You should declare some List<Panel> to contain all your panels, then you can navigate through them easily:
List<Panel> panels = new List<Panel>();
panels.AddRange(new[]{PageMainScreen, PageNewRegistration, PageSelectedPatient});
public void setMeVisible(string PanelName) {
var c = panels.FirstOrDefault(panel=>panel.Name == PanelName);
if(c != null) {
c.Visible = true;
if(currentShown != null) currentShown.Visible = false;
currentShown = c;
}
}
NOTE: Don't try complicating your UI unnecessarily. I want to mean that you should place all your panels on the same container (such as your form). That's the way we do, that way you can use the first approach, no need to loop, easy to maintain. You should also consider the Dock and the methods like BringToFront() and SendToBack() to show/hide the view.
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 have a method that finds all the controls, iterates through them, determines if they are a textbox,drop down list, etc.. retrieves their ID name, and depending on the ID name it will set a boolean statement (thus I would know if that section of the form is complete, and will email to a certain group of people) unfortunetly this is done with too many if statements and was wondering if I could get some help making this more manageable
protected void getEmailGroup()
{
Control[] allControls = FlattenHierachy(Page);
foreach (Control control in allControls)
{
if (control.ID != null)
{
if (control is TextBox)
{
TextBox txt = control as TextBox;
if (txt.Text != "")
{
if (control.ID.StartsWith("GenInfo_"))
{
GenInfo = true;
}
if (control.ID.StartsWith("EmpInfo_"))
{
EmpInfo = true;
}
}
}
if (control is DropDownList)
{
DropDownList lb = control as DropDownList;
if (lb.SelectedIndex != -1)
{
if (control.ID.StartsWith("GenInfo_"))
{
GenInfo = true;
}
if (control.ID.StartsWith("EmpInfo_"))
{
EmpInfo = true;
}
}
}
}
}
}
Why not just use the Control.FindControl(string) method?
private void Button1_Click(object sender, EventArgs MyEventArgs)
{
// Find control on page.
Control myControl1 = FindControl("TextBox2");
if(myControl1!=null)
{
// Get control's parent.
Control myControl2 = myControl1.Parent;
Response.Write("Parent of the text box is : " + myControl2.ID);
}
else
{
Response.Write("Control not found");
}
}
from: https://learn.microsoft.com/en-us/dotnet/api/system.web.ui.control.findcontrol
It is hard to understand the logic behind your code, but I'm sure it can be written easier. For example you can do something like this:
DropDownBox box = FlattenHierachy(Page)
.Where(c => c is DropDownList)
.Cast<DropDownList>()
.Where(d => d.SelectedIndex != -1)
.FirstOrDefault();
if (box != null)
{
if (box.ID.StartsWith("GenInfo_"))
{
GenInfo = true;
}
if (box.ID.StartsWith("EmpInfo_"))
{
EmpInfo = true;
}
}
Obviously you can make this generic if you extract the lambda expression from the seconde Where call. So you could reuse it for different types. That's the solution which is as close to your code as possible, but I guess it would be a better idea to use a recursive method traversing the page and giving that method your predicates as lambda expressions.
Cleaned up you code a little to only include each check once.
protected void getEmailGroup()
{
Control[] allControls = FlattenHierachy(Page);
foreach (Control control in allControls)
{
if (control.ID != null &&
((control is TextBox && ((TextBox)control).Text = "" )
|| (control is DropDownList && ((DropDownList)control).SelectedIndex != -1 ))
{
if (control.ID.StartsWith("GenInfo_"))
GenInfo = true;
if (control.ID.StartsWith("EmpInfo_"))
EmpInfo = true;
}
}
}
}
Instead of using the Lambda expression I have created a method that handles the control for me, and depending on the name of the control, it sets that section to be true
public bool setGroup(Control ctrl)
{
isAControl = false;
//set a section to true, so it will pull the html
if (ctrl.ID.StartsWith("GenInfo_"))
{
GenInfo = true;
lstControls.Add(ctrl.ID.Replace("GenInfo_", ""));
isAControl = true;
return isAControl;
}
here is a small snippet of my code I only want to check for certain controls(to speed things up) and I go through each control as each control has a different way to get the value (textbox would use .text where dropdownlist would use .selectedValue)
if(control is TextBox || control is DropDownList || control is RadioButton || control is RadioButtonList
|| control is CheckBox || control is CheckBoxList)
{
if (control is TextBox)
{
TextBox txt = control as TextBox;
if (txt.Text != "" && txt.Text != "YYYY/MM/DD")
{
setGroup(control);
if (isAControl)
{
string controlNoGroup = lstControls.Last();
strHtml = strHtml.Replace("#" + (controlNoGroup.ToString()) + "#", txt.Text);
}
}
}
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.