How to reduce this IF-Else ladder in c# - c#

This is the IF -Else ladder which I have created to focus first visible control on my form.According to the requirement any control can be hidden on the form.So i had to find first visible control and focus it.
if (ddlTranscriptionMethod.Visible)
{
ddlTranscriptionMethod.Focus();
}
else if (ddlSpeechRecognition.Visible)
{
ddlSpeechRecognition.Focus();
}
else if (!SliderControl1.SliderDisable)
{
SliderControl1.Focus();
}
else if (ddlESignature.Visible)
{
ddlESignature.Focus();
}
else
{
if (tblDistributionMethods.Visible)
{
if (chkViaFax.Visible)
{
chkViaFax.Focus();
}
else if (chkViaInterface.Visible)
{
chkViaInterface.Focus();
}
else if (chkViaPrint.Visible)
{
chkViaPrint.Focus();
}
else
{
chkViaSelfService.Focus();
}
}
}
Is there any other way of doing this. I thought using LINQ will hog the performance as i have to tranverse the whole page collection. I am deep on page which has masterpages.Please suggest.

I think your tree is good. This certainly looks like a logic tree that can be simplified, and you have a good sense of smell to be suspicious of it. However, it seems to be that the logic tree reflects what you need. The logic really is this convoluted, and this is the conditional framework that C# gives you to handle this situation. I don't think it can be improved.
If you had a simple list of controls that should have the focus, and you wanted to give focus to the first visible control in the list, you could do this:
(From c in ListOfControls
Where c.visible = true
Select c).First.Focus();
But, it appears you have some additional criteria, so that wouldn't work.

Two approaches:
Iterate controls and set focus if visible
Use TabIndex and set focus to first. Then focus should fall to first visible control

If you're just trying to focus the first visible control on the form, then I would replace the entire ladder with a single loop:
foreach (Control c in Controls)
{
if (c.Visible)
{
c.Focus();
break;
}
}
If you need to focus an inner control, use a recursive method:
bool FocusFirst(ControlCollection controls)
{
foreach (Control c in controls)
{
if (c.Visible)
{
c.Focus();
FocusFirst(c.Controls);
break;
}
}
}

You could return after you meet your criteria, for example:
if (ddlTranscriptionMethod.Visible)
{
ddlTranscriptionMethod.Focus();
return;
}
if (ddlSpeechRecognition.Visible)
{
ddlSpeechRecognition.Focus();
return;
}
etc..

You can iterate controls and set focus if visible. But I would suggest you to use state pattern for better code readability.

All your doing is setting the focus which is client-side functionality. I would personally do this in javascript (using jQuery). ASP.NET controls that aren't set to visible aren't rendered in the HTML, so you could look for the existence of those elements or there might be an easier way if you're just looking for the first visible, enabled control.

What about a jumpto seems like you could use that here.

When the list of items to evaluate is large (and sometimes it's very large), I try to separate the order of evaluation from the conditional logic, something like this:
List<WebControl> wcBasics = new List<WebControl>();
wcBasics.Add(ddlTranscriptionMethod);
wcBasics.Add(ddlSpeechRecognition);
wcBasics.Add(ddlESignature);
List<CheckBox> checks = new List<CheckBox>();
checks.Add(chkViaFax);
checks.Add(chkViaInterface);
checks.Add(chkViaPrint);
private void Focus()
{
foreach (WebControl c in wcBasics)
if (c.Visible) {
c.Focus();
return;
}
if (!tblDistributionMethods.Visible) return;
foreach (CheckBox chk in checks)
if (chk.Visible) {
chk.Focus();
return;
}
}
chkViaSelfService.Focus();
}

Here is a slightly different take on the problem. First, define an interface to represent controls which may or may not be focused:
public interface IFormControl
{
bool Focus();
}
Then create an implementation which handles the easy cases:
public class FormControl : IFormControl
{
private readonly Control _control;
public FormControl(Control control)
{
_control = control;
}
public bool Focus()
{
if(_control.Visible)
{
_control.Focus();
}
return _control.Visible;
}
}
And create another which handles the more difficult cases:
public class DependentFormControl : IFormControl
{
private readonly Control _control;
private readonly Func<bool> _prerequisite;
public DependentFormControl(Control control, Func<bool> prerequisite)
{
_control = control;
_prerequisite = prerequisite;
}
public bool Focus()
{
var focused = _prerequisite() && _control.Visible;
if(focused)
{
_control.Focus();
}
return focused;
}
}
Then, create an extension method which sets the focus on the first control in a sequence:
public static void FocusFirst(this IEnumerable<IFormControl> formControls)
{
var focused = false;
foreach(var formControl in formControls)
{
if(formControl.Focus())
{
break;
}
}
}
And finally, create the set of controls to be focused:
var controls = new FormControl[]
{
new FormControl(ddlTranscriptionMethod),
new FormControl(ddlSpeechRecognition),
new DependentFormControl(SliderControl1, () => !SliderControl1.SliderDisable),
new FormControl(ddlESignature),
new DependentFormControl(chkViaFax, () => tblDistributionMethods.Visible),
new DependentFormControl(chkViaInterface, () => tblDistributionMethods.Visible),
new DependentFormControl(chkViaPrint, () => tblDistributionMethods.Visible),
new DependentFormControl(chkViaSelfService, () => tblDistributionMethods.Visible)
};
controls.FocusFirst();
You can create the set of controls when your page loads and just call .FocusFirst() whenever necessary.

This is a classic state machine. By setting up a machine it can make the code easier to understand and maintain, though it may add to the total lines.
I won't get into the specifics of your code. By determining what state the user is operating in, you can programmatically change the differing values in an understandable specific state fashion. (Pseudo code below)
enum FormStates
{
Initial_View
Working_View
Edit_View
Shutdown_View
};
{ // Somewhere in code
switch (theCurrentState)
{
case Initial_View :
Control1.Enabled = true;
Control2.Enabled = true;
theCurrentState = Working_View;
break;
case Working_View
if (string.empty(Contro1.Text) == false)
{
Control2.Enabled = false;
Speachcontrol.Focus();
theCurrentState = Edit_view;
}
else // Control 2 is operational
{
Control1.Enabled = false;
SliderControl.Focus();
}
case Edit_View:
...
break;
break;
}
By organizing the code in logical steps, it makes it easier to add more states without jeopardizing an huge if/else.

Related

How to find first editable control to focus on in a page

My current function only finds first Textbox which is not totally correct. Control class doesn't have IsEditable property.
private static Control FindFocusableControl(Control CurrentControl)
{
if (CurrentControl.Visible)
{
if (CurrentControl is TextBox)
{
return CurrentControl;
}
if (CurrentControl.HasControls())
{
foreach (Control CurrentChildControl in CurrentControl.Controls)
{
Control focusableControl = FindFocusableControl(CurrentChildControl);
if (focusableControl != null)
{
return focusableControl;
}
}
}
}
return null;
}
On the first recursion for the first child control, the child control is probably not visible so the routine exits.
if (CurrentControl.Visible)
You might need a different kind of check at this point, perhaps check for textbox first, then if visible.

Minimize coding for validation in C#

I'm trying to validate a set of Textboxes in a winform application.
if(string.IsNullOrEmpty(txtCarbohydrate.Text){
//todo
}
but there are several Textboxes in my form to be validated for emptiness , not only in the current win form but other forms too. how can i create a method or a class that can validate several Textboxes and
be reusable across application?
EDIT : i wrote somthing like this , any suggestion to make it better ?
class ValidateEmpty
{
bool res = false;
//List<object> txt = new List<object>();
List<string> st = new List<string>();
public List<string> St
{
get { return st; }
set { st = value; }
}
public ValidateEmpty(List<string> _str)
{
this.st = _str;
}
public bool checkEmpty()
{
bool res = false;
for (int i = 0; i < St.Count(); i++ )
{
if(string.IsNullOrEmpty(St[i]))
{
res= true;
}
}
return res;
}
}
}
`
You could put them in a list and then loop through the list.
List<TextBox> TextBoxes=new List<TextBox>() {txtCarbohydrate, txtProtein, txtFat};
foreach(TextBox tb in TextBoxes)
{
if(String.IsNullOrEmpty(tb.Text)
{
//do something
}
}
Based on your edit, you want to return a Boolean (it's really hard to understand your code and what you're trying to accomplish, you need to be clear and concise!) to indicate if a TextBox was empty or not. Here's how you could create a method to do that...
public static bool IsThereAnEmptyTextBox(List<TextBox> textBoxes)
{
bool emptyfound=false;
foreach(TextBox tb in textboxes)
{
if(String.IsNullOrEmpty(tb.Text)
{
emptyfound=true;
}
}
return emptyfound;
}
You can call this function from any class, if you put this function in a Utility class or in a base class etc. If you want to combine it with paqogomez's answer you can call it from a form like this...
bool emptyfound=MyUtilities.IsThereAnEmptyTextBox(myForm.Controls.OfType<TextBox>().ToList());
I think this is a terrible way of going about it, but I'm trying to demonstrate how you could do what you've asked for.
To grab all textboxes in a single form use Controls.OfType<T>
var controls = myForm.Controls.OfType<TextBox>();
foreach(TextBox tb in controls)
{
//do validation
}
Depending on the type of validation you are doing, you can also do as #RandRandom suggests and put the required attribute on your textboxes. This will force the user to put text in before its submitted.

Generic way to get the values from multiple controls

I have a Form with multiple different controls like ComboBox, TextBox and CheckBox. I am looking for a generic way to get values from these controls while looping over them.
For example, something like this:
foreach(Control control in controls)
{
values.Add(control.Value);
}
Is it possible or do I need to treat each control separately?
Try this:
Panel myPanel = this.Panel1;
List<string> values = new List<string>();
foreach (Control control in myPanel.Controls)
{
values.Add(control.Text);
}
But make sure you get only the controls you want. You can check the type just like
if(control is ComboBox)
{
// Do something
}
The Text solution is OK if every Control is a TextBox, but if you have some Label you'll end up with the text of the labels among the values, unless you fill your code with if's. A better solution could be to define a set of delegates that for each kind of Control return what is considered the value (e.g. Text for the TextBox and Checked for the CheckBox), put them in a dictionary, and use them to get the value for each control. The code could be something like this:
public delegate object GetControlValue(Control aCtrl);
private static Dictionary<Type, GetControlValue> _valDelegates;
public static Dictionary<Type, GetControlValue> ValDelegates
{
get
{
if (_valDelegates == null)
InitializeValDelegates();
return _valDelegates;
}
}
private static void InitializeValDelegates()
{
_valDelegates = new Dictionary<Type, GetControlValue>();
_valDelegates[typeof(TextBox)] = new GetControlValue(delegate(Control aCtrl)
{
return ((TextBox)aCtrl).Text;
});
_valDelegates[typeof(CheckBox)] = new GetControlValue(delegate(Control aCtrl)
{
return ((CheckBox)aCtrl).Checked;
});
// ... other controls
}
public static object GetValue(Control aCtrl)
{
GetControlValue aDel;
if (ValDelegates.TryGetValue(aCtrl.GetType(), out aDel))
return aDel(aCtrl);
else
return null;
}
Then you can write:
foreach (Control aCtrl in Controls)
{
object aVal = GetValue(aCtrl);
if (aVal != null)
values.Add(aVal);
}

How to clear or regenerate window form after submitting using c#?

I am using student record entry window form. I want that after submitting data, all fields of form(i.e radio button, combobox etc) and messages(warning and successful) should be reset so that new user can add data.
is their any built in function to reset form in csharp?
or I have to write clear method for this?
or can I regenerate the form?
You must first clear the controls and then use InitializeComponent method to work perfectly.
this.Controls.Clear();
this.InitializeComponent();
This you can achieve in two ways:-
1) you can clear the fields of the form in one method. say public void clear() And whenever you want to clear the form simply call this method.
2) In second way you can destory the instance of the form and generate the new instance of the same form and then show this.
I will recomeded 1st one for you.
This is what I used to create in my every page
private void ClearControls()
{
try
{
txtUserName.Text = string.Empty;
//txtPassword.Text = string.Empty;
txtFName.Text = string.Empty;
txtMName.Text = string.Empty;
txtLName.Text = string.Empty;
lblUserType.Text = string.Empty;
btnSave.Text = "Save";
fnMessage(false, "");
}
catch (Exception ex)
{
fnMessage(true, ex.Message);
}
}
Thanks
Implement data binding to a given object (good starting point here)
For resetting the form, create a new object and the binding will do it for you.
HTH
You can either re-create the form instance, or perhaps try something similar to this (untested):
foreach (Control ctl in this.Controls)
{
switch (ctl.GetType().ToString())
{
case "TextBox":
ctl.Text = null;
break;
case "ComboBox":
ctl.Text = null;
break;
}
}
Clearly, you can include as many different types of control as you wish and introduce other critieria (i.e. where control name begins with 'xyz' or where control resides within a particular panel).
Compared to other suggestions, the advantage of this approach is that if you have dozens of the same control type (typically textboxes), a few lines of code cover the lot. Additionally, if you add more controls of the covered types, you don't need to revisit the code to update it. Perhaps you could even create it as an extension method of your forms?
If form creation doesn't take too much resources it is easier to create new instance.
As i had same problem and my from was having nested controls. I tried CJM's method and it worked however i had to write a recursive function because of controls nesting (tab controls, containers, user controls etc)
Try out the following snippet if you want to clear whole form recursively
private void clearRecursive(Control control)
{
foreach (Control subcontrol in control.Controls)
{
switch (subcontrol.GetType().ToString().Replace("System.Windows.Forms.", ""))
{
case "TextBox":
TextBox textBox = (TextBox)subcontrol;
textBox.Text = null;
break;
case "ComboBox":
ComboBox comboBox = (ComboBox)subcontrol;
if (comboBox.Items.Count > 0)
comboBox.SelectedIndex = 0;
break;
case "CheckBox":
CheckBox checkBox = (CheckBox)subcontrol;
checkBox.Checked = false;
break;
case "RadioButton":
RadioButton radioButton = (RadioButton)subcontrol;
radioButton.Checked = false;
break;
case "TreeView":
TreeView tv = (TreeView)subcontrol;
foreach (TreeNode node in tv.Nodes)
{
node.Checked = false;
CheckChildren(node, false);
}
break;
case "ListBox":
ListBox listBox = (ListBox)subcontrol;
listBox.Items.Clear();
break;
case "CheckedListBox":
CheckedListBox chklstbox = (CheckedListBox)subcontrol;
for (int i = 0; i < chklstbox.Items.Count; i++)
{
chklstbox.SetItemCheckState(i, CheckState.Unchecked);
}
break;
}
if (subcontrol.HasChildren)
clearRecursive(subcontrol);
}
}
Call InitializeComponent method and Form_Load method.

Accessing a UserControls methods from its object

I have created a UserControl called AutorControl with a method to clear its textbox:
public void LimpiarAutorTextbox()
{
textBox1.Text = "";
}
Then my intention is from another form with a Panal, using a for loop add X ammount of the above user control. Then I want to call the UserControls method: "LimpiarAutorTextbox" (which is just a method for clearing the text of the textbox) using a foreach loop like this, however it's not working. I'm not sure what to do in this case:
AutorControl usercontrolAutorControl = new AutorControl();
private override void ClearControls()
{
txtTitulo.Text = "";
//Panel1 will only hold controls of the same type: "AutorControl"
foreach (Control X in panel1.Controls)
{
X as AutorControl;//?????? I want to access each created usercontrols' method.
}
}
The panel will always hold a usercontrol of AutorControl, never anything else. How can I achieve this programatically?
Thanks.
Your line here is fine:
X as AutorControl
just add:
(X as AutorControl).LimpiarAutorTextbox()
that should do the trick.
Also, I know you said that there would only be AutorControls in there, but you may want to do something more like this:
AutorControl currentControl == X as AutorControl;
if (AutorControl != null)
{
currentControl.LimpiarAutorTextbox();
}
Or alternatively, you can change your declaration of the for foreach loop to do the cast for you:
foreach(AutorControl currentControl in form.Controls)
{
if (currentControl != null)
{
currentControl.LimpiarAutorTextbox();
}
}
Some alternatives :)

Categories

Resources