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);
}
}
}
Related
private bool LoopOverControls(bool reset, bool checkIfEmpty)
{
bool results;
foreach (Control ctrl in this.Controls)
{
if ((ctrl as TextBox) != null)
{
if (reset == true)
(ctrl as TextBox).Text = "";
if (checkIfEmpty == true)
results = true;
}
}
return results;
}
I want to use the method in some places in the code. Instead making loop over the controls over again each time i want to make a method i can call.
The method was before:
private void LoopOvercontrols(bool reset, bool checkIfEmpty)
{
foreach (Control ctrl in this.Controls)
{
if ((ctrl as TextBox) != null)
{
if (reset == true)
(ctrl as TextBox).Text = "";
if (checkIfEmpty == true)
}
}
}
And this is the places i'm using the loop over the controls in my code the first place is in the constructor: I check if the textBoxes are not empty then do something in this case change btnReset enable true.
foreach (Control ctrl in this.Controls)
{
if ((ctrl as TextBox) != null)
{
(ctrl as TextBox).TextChanged += on_TextChanged;
if ((ctrl as TextBox).Text != "")
{
btnReset.Enabled = true;
}
}
}
Then inside another event this time i check if the textboxes are empty and set the btnReset enable to false:
foreach (Control ctrl in this.Controls)
{
if ((ctrl as TextBox) != null)
{
(ctrl as TextBox).TextChanged += on_TextChanged;
if ((ctrl as TextBox).Text == "")
{
btnReset.Enabled = false;
}
}
}
So far i'm looping over the textBoxes in two places but i might want to loop over them again later in other places. The problem is how to make the method LoopOverControls so i can decide with a bool and maybe other properties some cases and using buttons or other controls within the decitions ?
You can write a method that receive the action to be executed as parameter.
The work for this new method is to enumerate all the textboxes and call the action method for each one.
public void TextBoxEnumerateAndAction(Action<TextBox> execute)
{
// Get just the TextBoxes, no need of ugly casts...
foreach(TextBox t in this.Controls.OfType<TextBox>())
{
execute?.Invoke(t);
// This part is common to every textbox, so it can stay inside the
// enumeration loop....
btnReset.Enabled = !string.IsNullOrEmpty(t.Text)
}
}
Now define the Action methods to pass to TextBoxEnumerateAndAction
void AddTextChangedHandler(TextBox t)
{
t.TextChanged += on_TextChanged;
}
void RemoveTextChangedHandler(TextBox t)
{
t.TextChanged -= on_TextChanged;
}
So, everywhere you need to add or remove the TextChanged handler you could call
TextBoxEnumerateAndAction(AddTextChangedHandler);
Or if you have more fancy situations, you could simply define another action to pass to TextBoxEnumerateAndAction
I suggest you just add some comfort methods to contain the looping and type selection logic and allow callers to pass in delegate function to control the applied logic.
for example:
public void ActOn<TControl>(Action<TControl> applyFunction)
where TControl : Control
{
if (applyFunction == null) { throw new ArgumentNullException(nameof(applyFunction)); }
var controlsOfChosenType = this
.Controls
.OfType<TControl>();
foreach (var control in controlsOfChosenType)
{
applyFunction(control);
}
}
Then you can use it like this:
ActOn<TextBox>(textbox => textbox.Text = DateTime.Now.ToString());
I'm using following code to clear all text boxes in a form.
protected static IEnumerable<Control> GetAllChildren(Control root)
{
var stack = new Stack<Control>();
stack.Push(root);
while (stack.Any())
{
var next = stack.Pop();
foreach (Control child in next.Controls)
stack.Push(child);
yield return next;
}
}
internal static void ResetTextBoxes(Control root, string resetWith = "", params TextBox[] except)
{
try
{
foreach (TextBox txt in GetAllChildren(root).OfType<TextBox>())
{
foreach (TextBox txtException in except)
{
if (txtException.Name != txt.Name)
{
txt.Text = resetWith == "" ? string.Empty : resetWith;
}
}
}
}
catch (Exception ex) { throw ex; }
}
I tried to separate some special text boxes those I don't want to be cleared by using params, but still it clears all the boxes. Need help, please.
Shorter version of GetAllChildren:
protected static IEnumerable<Control> GetAllChildren(Control root) {
return new Control[] { root }
.Concat(root.Controls
.OfType<Control>()
.SelectMany(item => GetAllChildren(item)));
}
And shorter Linq:
var source = GetAllChildren(root)
.OfType<TextBox>()
.Where(ctrl => !except.Contains(ctrl));
foreach (var textBox in source)
textBox.Text = resetWith;
The problem with your current implmentation is in the inner loop:
foreach (TextBox txtException in except)
if (txtException.Name != txt.Name)
txt.Text = resetWith == "" ? string.Empty : resetWith;
if you have at least two exceptions with different names that condition
txtException.Name != txt.Name
will be inevitably satisfied (any txt.Name either not equal 1st exception or 2nd one)
This is happening since you are testing all the elements the first collection against all the elements of the second collection, so even if a textbox exists in the except array, it's name will not match the other textboxes there.
Use Linq's Any extension method instead:
internal static void ResetTextBoxes(Control root, string resetWith = "", params TextBox[] except)
{
foreach (TextBox txt in GetAllChildren(root).OfType<TextBox>())
{
if(!except.Any(t => t.Name == txt.Name))
{
txt.Text = resetWith == "" ? string.Empty : resetWith;
}
}
}
}
Here is the function I use:
public void ClearAllFields(Control con)
{
foreach (Control c in con.Controls)
{
if (c is TextBox && c != null)
{
if (c.Name != "specialtxtbox1name" && c.Name != "specialtxtbox2name" && c.Name != "nameoftextbox") // just put in the name of special textBoxes between double quotes
c.Text = "";
}
else
{
ClearAllFields(c);
}
}
}
Simply call the function wherever needed
Example:
private void InsertDatathenClearForm_Click(object sender, EventArgs e)
{
//Code
//.
//.
File.Create(filepath);
MessageBox.Show("All Textboxes Data saved successfully");
//Clear fxn
ClearAllFields(this);
}
.
.
}
I'm just wondering really.
I have series of if statements that check
if textboxes are empty (or have results strings) after i pass SQL
results to then
.
if (IncidentData.Tables[0].Rows[0]["Property Category"].ToString()
== "RoadVehicle")
{
lbl_alarmOperated.Visible = false; tb_alarmOperated.Visible = false;
}
else
{
lbl_alarmOperated.Visible = true;
tb_alarmOperated.Visible = true;
}
I have been looking into controls and seeing if i can do a check on all textboxes and hide them if they are empty (instead of writing loads of if statements)
i have this at the moment:
public void ChecknHide()
{
HideTextBoxes(this);
}
protected void HideTextBoxes(Control ctrl)
{
foreach (var c in ctrl.Controls)
{
if (c is TextBox) ((TextBox)c).Text = String.Empty;
{
((TextBox)c).Visible = false;
}
}
}
Its mostly put together from reading posts on here. But I've ran into an issue. When i compile and go to view the page i get this:
Unable to cast object of type 'ASP.masterpage_master' to type
'System.Web.UI.WebControls.TextBox'.
Any ideas whats going wrong?
The statement after the if isn't part of the condition. This causes all controls to be casted to a TextBox. You should be able to fix it like so:
protected void HideTextBoxes(Control ctrl)
{
foreach (var c in ctrl.Controls)
{
if (c is TextBox && ((TextBox)c).Text == String.Empty)
{
((TextBox)c).Visible = false;
}
}
}
Weird code line:
if (c is TextBox) ((TextBox)c).Text = String.Empty;
Try something like:
protected void HideTextBoxes(Control ctrl)
{
//Iterate over controlls
foreach (var c in ctrl.Controls)
{
//Check for Textbox controls with the .Text property equal to Null or Empty.
if (c is TextBox && string.IsNullOrEmpty(((TextBox)c).Text))
{
//Set visibility of Textbox control to invisible.
((TextBox)c).Visible = false;
}
}
}
You're checking if c is a TextBox, but then trying to cast c as a TextBox and set it to String.Empty in the same line, regardless of whether it actually is a TextBox.
if (c is TextBox) ((TextBox)c).Text = String.Empty;
I'm working in VS 2012 using C# and WinForms for my application, and I'm curious what sort of routine I should use to clear all methods of entering data I have, which includes textboxes, comboboxes, and date time pickers. I've Googled and found a few 'answers', but none seem to work or actually prove helpful.
[EDIT]:
I kept researching and actually found a helpful method that I just had to add some ifs to to get what I wanted:
private void ResetFields()
{
foreach (Control ctrl in this.Controls)
{
if (ctrl is TextBox)
{
TextBox tb = (TextBox)ctrl;
if (tb != null)
{
tb.Text = string.Empty;
}
}
else if (ctrl is ComboBox)
{
ComboBox dd = (ComboBox)ctrl;
if (dd != null)
{
dd.Text = string.Empty;
dd.SelectedIndex = -1;
}
}
else if (ctrl is DateTimePicker)
{
DateTimePicker dtp = (DateTimePicker)ctrl;
if (dtp != null)
{
dtp.Text = DateTime.Today.ToShortDateString();
}
}
}
}
Something like this:
void ClearThem(Control ctrl)
{
ctrl.Text = "";
foreach (Control childCtrl in ctrl.Controls) ClearThem(childCtrl);
}
And then:
ClearThem(this);
Another option:
Create a class deriving from Panel, with all of what you need on it, and Dock it in the Form. When you need to "refresh" - simply replace that Panel with a new instance of that Panel.
You could just loop in all the controls of the form and clear depending on the control type
We can clear all Textboxes, Comboboxes but not the DateTimePicker
If you want to clear the DateTimePicker you must set the properties:
Format = Custom, CustomFormat = " " and in time that you want to select a date in DateTimePicker
private void dateTimePicker1_CloseUp(object sender, EventArgs e)
{
dateTimePicker1.Format = DateTimePickerFormat.Short;
}
And this could be the solution:
public static void ClearAll(Control control)
{
foreach (Control c in control.Controls)
{
var texbox = c as TextBox;
var comboBox = c as ComboBox;
var dateTimePicker = c as DateTimePicker;
if (texbox != null)
texbox.Clear();
if (comboBox != null)
comboBox.SelectedIndex = -1;
if (dateTimePicker != null)
{
dateTimePicker.Format = DateTimePickerFormat.Short;
dateTimePicker.CustomFormat = " ";
}
if (c.HasChildren)
ClearAll(c);
}
}
Loop through your form controls, match them to your types and set it to "" or null;
We have some legacy code that needs to identify in the Page_Load which event caused the postback.
At the moment this is implemented by checking the Request data like this...
if (Request.Form["__EVENTTARGET"] != null
&& (Request.Form["__EVENTTARGET"].IndexOf("BaseGrid") > -1 // BaseGrid event ( e.g. sort)
|| Request.Form["btnSave"] != null // Save button
This is pretty ugly and breaks if someone renames a control. Is there a better way of doing this?
Rewriting each page so that it does not need to check this in Page_Load is not an option at the moment.
This should get you the control that caused the postback:
public static Control GetPostBackControl(Page page)
{
Control control = null;
string ctrlname = page.Request.Params.Get("__EVENTTARGET");
if (ctrlname != null && ctrlname != string.Empty)
{
control = page.FindControl(ctrlname);
}
else
{
foreach (string ctl in page.Request.Form)
{
Control c = page.FindControl(ctl);
if (c is System.Web.UI.WebControls.Button)
{
control = c;
break;
}
}
}
return control;
}
Read more about this on this page:
http://ryanfarley.com/blog/archive/2005/03/11/1886.aspx
In addition to the above code, if control is of type ImageButton then add the below code,
if (control == null)
{ for (int i = 0; i < page.Request.Form.Count; i++)
{
if ((page.Request.Form.Keys[i].EndsWith(".x")) || (page.Request.Form.Keys[i].EndsWith(".y")))
{ control = page.FindControl(page.Request.Form.Keys[i].Substring(0, page.Request.Form.Keys[i].Length - 2)); break;
}
}
}
I am just posting the entire code (which includes the image button / additional control check that causes postback). Thanks Espo.
public Control GetPostBackControl(Page page)
{
Control control = null;
string ctrlname = page.Request.Params.Get("__EVENTTARGET");
if ((ctrlname != null) & ctrlname != string.Empty)
{
control = page.FindControl(ctrlname);
}
else
{
foreach (string ctl in page.Request.Form)
{
Control c = page.FindControl(ctl);
if (c is System.Web.UI.WebControls.Button)
{ control = c; break; }
}
}
// handle the ImageButton postbacks
if (control == null)
{ for (int i = 0; i < page.Request.Form.Count; i++)
{
if ((page.Request.Form.Keys[i].EndsWith(".x")) || (page.Request.Form.Keys[i].EndsWith(".y")))
{ control = page.FindControl(page.Request.Form.Keys[i].Substring(0, page.Request.Form.Keys[i].Length - 2)); break;
}
}
}
return control;
}