1

I created a custom control that inherits from TextBox.

I have a ValidateTextBoxEntry method that is called by the Validating event.

Normally, I would use the Property editor in the visual editor to add ValidateTextBoxEntry to the Validating event of each and every instance of the custom control.

Instead, I would rather add a line in the constructor of the custom control to all the event handler automatically, something like this:

    public CustomTextBox()
    {
        InitializeComponent();
        this.Validating += 
        new System.ComponentModel.CancelEventHandler(ValidateTextBoxEntry);
   }

What is an elegant way of adding a Validating event handler to all instances of a custom control?

Davide Andrea
  • 1,357
  • 2
  • 15
  • 39
  • 1
    Why should your custom textbox class/constructor depend on the MainForm class? This is a code smell and is probably indication of bad software design. Why can't you implement ValidateTextBoxEntry as a method of your custom textbox or as a method of a textbox-related helper class -- after all, ValidateTextBoxEntry is providing textbox-related functionality, or? –  Aug 04 '14 at 21:47
  • > This is a code smell ---- OK, I can accept that. Hence, the request for help. – Davide Andrea Aug 04 '14 at 21:54
  • > Why can't you implement ValidateTextBoxEntry as a method of your custom textbox ---- Yes, I can, and I would prefer that in fact. But that by itself doesn't answer my question: what's the right way of adding the Validating event handler through the constructor? – Davide Andrea Aug 04 '14 at 21:57
  • 1
    Yes, it does answer your question. Because this would be one of the right approaches/ways to do it. But, perhaps there is a misunderstanding with regard to what you are seeking for, in which case i would ask you to clarify your problem, so that better targeted advice can be given... –  Aug 04 '14 at 21:58
  • > clarify your problem --- Let me try this: "What would be the most elegant line in the constructor be?" – Davide Andrea Aug 04 '14 at 22:06
  • 1
    I really do not understand your problem, i am sorry. Syntactically, adding an event handler to an event is like that: `someEvent += someEventHandler;`. That's as elegant as it can be... (and that's what you did already.) Regarding tying your textbox constructor to the MainForm class, well, look at my first comment. Do you perhaps have problems understanding the difference between static and instance (i.e. non-static) methods? –  Aug 04 '14 at 22:11
  • > Do you perhaps have problems understanding the difference between static and instance (i.e. non-static) methods? --- I can see why you would think that, but, no, I don't think I do. – Davide Andrea Aug 04 '14 at 22:15
  • > That's as elegant as it can be --- OK, I'll go with that. Thanks! – Davide Andrea Aug 04 '14 at 22:16
  • 1
    @elgonzo I moved the handler into the custom control class, as you suggested, and all is well. Thank you. – Davide Andrea Aug 04 '14 at 22:53

3 Answers3

2
    this.Validating += 
    new System.ComponentModel.CancelEventHandler(ValidateTextBoxEntry);

This is not elegant. Events in .NET are an implementation of the Observer Pattern. You expose them so other code can subscribe the event and, in this case, customize the validation. It doesn't make sense to have to listen to your own events.

To accommodate, every Xxxx event has an OnXxxx() method. You can call this method yourself to cause the Validating event to be raised. Or, more commonly in this case, you leave it up to Winforms to call it for you. A sample implementation could look like this:

    protected override void OnValidating(CancelEventArgs e) {
        if (this.Text.Length == 0) e.Cancel = true;
        else base.OnValidating(e);
        DisplayWarning(e.Cancel);
    }

Note how the event handling is customized here. It implements the rule that it is never valid to have an empty string in the textbox. At which point there is no reason anymore to call base.Onvalidating(), you don't want an event handler to override that rule. And further extended by automatically taking care of displaying a hint to the user that his data-entry needs to be worked on.

Doing it this way gives you control over the order in which code runs, that can be very important.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
1

I agree with elgonzo in the comments that this probably isn't the correct approach but I think you could achieve it with an interface.

If you define an interface that contains the handler definition and make each form implement that interface you can then wire the handler up from within the control if you can find the right form. For that you can keep iterating around the Parent property until it's null. I really don't think it's worth the effort though:

//the interface
public interface IShouldntDoThis
{
    void MyTextBox_Validating(object sender, CancelEventArgs e);
}

//the control
public class MyTextBox : TextBox
{
    public MyTextBox()
    {

    }

    protected override void InitLayout()
    {
        base.InitLayout();

        Control parent = this.Parent;
        while (parent.Parent != null)
        {
            parent = parent.Parent;
        }

        //parent should now be the Form
        IShouldntDoThis test = parent as IShouldntDoThis;
        if (test != null)
        {
            this.Validating += test.MyTextBox_Validating;
        }
    }
}

//the form
public partial class MainForm : Form, IShouldntDoThis
{
    public Form1()
    {
        InitializeComponent();
    }

    public void MyTextBox_Validating(object sender, CancelEventArgs e)
    {
        throw new NotImplementedException();
    }
}
petelids
  • 12,305
  • 3
  • 47
  • 57
  • You actually answered another question I had but I didn't ask: how can a Control get a handle to the Frame. Thank you. – Davide Andrea Aug 04 '14 at 22:20
  • 1
    You're welcome @DavideAndrea, did you try the above for wiring your handler up? I wrote a test quickly and it should work for you. – petelids Aug 04 '14 at 22:25
0

Well, as it turns out, adding a line in the constructor (as I showed in the question), is the right approach (as stated by elgonzo - thank you).

The key is to make the event handler be a method of the class itself.

Davide Andrea
  • 1,357
  • 2
  • 15
  • 39