//list the controls from the main form
foreach (Control c in Controls)
{
if (c is ComboBox)
{
((ComboBox)c).SelectedIndex = -1;
}
else if (c is TextBox)
{
((TextBox)c).Text = "";
}
else if (c is CheckBox)
{
((CheckBox)c).Checked = false;
}
//etc. with FIFTY different types to check against
}
If the duplicate code is inside a constructor, use Pull Up Constructor Body. If the duplicate code is similar but not completely identical, use Form Template Method. If two methods do the same thing but use different algorithms, select the best algorithm and apply Substitute Algorithm.
The conventional approach to reduce this kind of code duplication is to move the common code to a member function, which can be called from all the constructors. Usually, that member function is called init.
Duplication is bad, but… Good developers are precise and careful and understand the risks and pitfalls of duplication in a code base—see the DRY (don't repeat yourself) principle, from The Pragmatic Programmer: “The alternative is to have the same thing expressed in two or more places.
One way is to add three overloaded methods for specific types, cast to dynamic
, and make calls like this:
foreach (dynamic c in Controls) {
ClearOut(c);
}
...
private static void ClearOut(ComboBox c) {
c.SelectedIndex = -1;
}
private static void ClearOut(TextBox t) {
t.Text = string.Empty;
}
private static void ClearOut(CheckBox c) {
c.Checked = false;
}
Since c
is dynamic
, C# would postpone binding of the ClearOut
method to runtime, giving you a clean-looking code. The downside of this approach is that C# is unable to tell you at compile-time if one of the overloads is missing.
Use this method to set the properties of your controls:
public void Set(object obj, string property, object value)
{
//use reflection to get the PropertyInfo of the property you want to set
//if the property is not found, GetProperty() returns null
var propertyInfo = obj.GetType().GetProperty(property);
//use the C# 6 ?. operator to only execute SetValue() if propertyInfo is not null
propertyInfo?.SetValue(obj, value);
}
Call it like this:
foreach (Control c in Controls)
{
Set(c, "SelectedIndex", -1);
Set(c, "Text", "");
Set(c, "Checked", false);
}
You can create a lookup from each supported type to an action that clears controls of that type, you can then add handlers to that lookup for each supported type:
public class ControlClearer
{
private static Dictionary<Type, Action<Control>> lookup = new Dictionary<Type, Action<Control>>();
static ControlClearer()
{
AddMapping((TextBox control) => control.Text = "");
AddMapping((ComboBox control) => control.SelectedIndex = -1);
AddMapping((CheckBox control) => control.Checked = false);
}
private static void AddMapping<T>(Action<T> clearAction)
where T : Control
{
lookup[typeof(T)] = control => clearAction((T)(object)control);
}
public static void Clear<T>(T control)
where T : Control
{
//todo support case where T isn't in the dictionary
lookup[typeof(T)](control);
}
public static void Clear(Control control)
{
//todo support case where the type isn't in the dictionary
lookup[control.GetType()](control);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With