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 :-)

Was it helpful?

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top