Cast sender object in event handler using GetType().Name
-
10-07-2019 - |
Question
I have an event handler for a Textbox as well as for a RichTextBox. The code is identical, but
In handler #1 i do:
RichTextBox tb = (RichTextBox)sender
In handler #2 accordingly:
TextBox tb = (TextBox)sender
Doing so i can fully manipulate the sending control. What i want to know is how can i cast the sending object to Textbox or RichTextbox according to its type using
sender.GetType().Name
and then create the control at runtime and work with it. That way i only need one event handler function: less code, less errors, easier to maintain and DRY :-)
Solution
Depending on what properties you need, you could cast the sender as a TextBoxBase as both the TextBox and RichTextBox both inherit from that sub-class.
OTHER TIPS
You never have to cast. I used to think the same way when I started, this 'pattern' is incorrect, and not really logical.
Your best bet is to use something like:
if (sender is TextBox)
{
TextBox tb = (TextBox)sender;
}
else if (sender is RichTextBox)
{
RichTextBox rtb = (RichTextBox)sender;
}
else
{
// etc
}
I know this is a very old post but in Framework 4 you can cast the sender as a Control:
Control cntrl = (Control)sender;
cntrl.Text = "This is a " + sender.GetType().ToString();
Note you are only able to reference controls that all of the different controls have in common (ie Text).
RichTextBox textbox = sender as RichTextBox;
if (textbox != null)
{
// do stuff as a rtb
textbox.Text = "I'm a rtb";
return;
}
TextBox textbox = sender as TextBox;
if (textbox != null)
{
// do stuff as a textbox
textbox.Text = "I'm a textbox";
}
Casting can only be done at compile-time and thus you need to know the types that you wish to cast to at compile-time. A runtime Type (as returned by GetType()) can therefore not be used when casting.
If it is polymorphism you are looking for you could access the Name property through reflection. I wouldn't go that way though just to be able to reuse event handlers.
If you want strong typing, a common base class or interface on the two senders is the only way to go.
Rather than the type name you could use 'is'.
If you just want to know the type and don't need an object reference:
if (sender is RichTextBox)
{
// ...
}
else if (sender is TextBox)
{
// ...
}
However you generally do want the object: C#7 has a nice syntax that allows you to test and get the value inline:
if (sender is RichTextBox richTextBox)
{
richTextBox.Text = "I am rich";
}
else if (sender is TextBox textBox)
{
textBox.Text = "I am not rich";
}
You can also use an inline-temporary variable to handle the cast for you.
if (sender is RichTextBox tb)
{
// ... //
}
else if (sender is TextBox tb)
{
// ... //
}
If the code is identical, do you need to care? I wonder if casting to Control
wouldn't give you everything you need...
One complex handler is not necessarily better than several simple handlers. Either way, if you have to go this route, "as"/"is" is preferable (it isn't dependent on strings etc):
TextBox tb = sender as TextBox;
if(tb!=null) {/* TextBox specific code */}
...
if you dont want to repeat the code then you can cast both the controls, refactor the common actions to a separate method which takes TextBoxBase as an argument. And in your event handlers convert the controls to System.Windows.Forms.TextBoxBase as both controls are derived from the TexbBoxBase and call the method.
Please note If you need specific properties of any of these controls then this refactoring wont work.
Generic version of the above code:
public static void CastAndUse<T>(object item, Action<T> action) where T : class
{
T thing = item as T;
if (thing != null)
{
action(thing);
}
}
Used as:
CastAndUse(sender, new Action((foo) => foo = bar));
Not perfect, but handy.