Question

Is there any way for the main form to be able to intercept events firing on a subcontrol on a user control?

I've got a custom user-control embedded in the main Form of my application. The control contains various subcontrols that manipulate data, which itself is displayed by other controls on the main form. What I'd like is if the main form could be somehow informed when the user changes subcontrols, so I could update the data and the corresponding display elsewhere.

Right now, I am cheating. I have a delegate hooked up to the focus-leaving event of the subcontrols. This delegate changes a property of the user-control I'm not using elsewhere (in this cause, CausesValidation). I then have a delegate defined on the main form for when the CausesValidation property of the user control changes, which then directs the app to update the data and display.

A problem arises because I also have a delegate set up for when focus leaves the user-control, because I need to validate the fields in the user-control before I can allow the user to do anything else. However, if the user is just switching between subcontrols, I don't want to validate, because they might not be done editing.

Basically, I want the data to update when the user switches subcontrols OR leaves the user control, but not validate. When the user leaves the control, I want to update AND validate. Right now, leaving the user-control causes validation to fire twice.

Was it helpful?

Solution

The best practice would be to expose events on the UserControl that bubble the events up to the parent form. I have gone ahead and put together an example for you. Here is a description of what this example provides.

  • UserControl1
    • Create a UserControl with TextBox1
    • Register a public event on the UserControl called ControlChanged
    • Within the UserControl register an event handler for the TextBox1 TextChangedEvent
    • Within the TextChangeEvent handler function I call the ControlChanged event to bubble to the parent form
  • Form1
    • Drop an instance of UserControl1 on the designer
    • Register an event handler on UserControl1 for MouseLeave and for ControlChanged

Here is a screenshot illustrating that the ControlChanged event that I defined on the UserControl is available through the UX in Visual Studio on the parent Windows form.

Event Handlers for User Control http://friendfeed.s3.amazonaws.com/0d5a3968cb785625c8afb3974a8d84894c476291

OTHER TIPS

The best model for this sort of thing will be creating custom events on your user control, and raising them at the appropriate times.

Your scenario is fairly complex, but not unheard of. (I'm actually in a very similar mode on one of my current projects.) The way I approach it is that the user control is responsible for its own validation. I don't use CausesValidation; instead at the appropriate user control point, I perform validation through an override of ValidateChildren(). (This usually happens when the user clicks "Save" or "Next" on the user control, for me.)

Not being familiar with your user control UI, that may not be 100% the right approach for you. However, if you raise custom events (possibly with a custom EventArgs which specifies whether or not to perform validation), you should be able to get where you want to be.

You will need to wire up the events you care about capturing inside your user control, and publish them through some custom event properties on the user control itself. A simple example would be wrapping a button click event:

// CustomControl.cs
// Assumes a Button 'myButton' has been added through the designer

// we need a delegate definition to type our event
public delegate void ButtonClickHandler(object sender, EventArgs e);

// declare the public event that other classes can subscribe to
public event ButtonClickHandler ButtonClickEvent;

// wire up the internal button click event to trigger our custom event
this.myButton.Click += new System.EventHandler(this.myButton_Click);
public void myButton_Click(object sender, EventArgs e)
{
  if (ButtonClickEvent != null)
  {
    ButtonClickEvent(sender, e);
  }
}

Then, in the Form that uses that control, you wire up the event like you would any other:

// CustomForm.cs
// Assumes a CustomControl 'myCustomControl' has been added through the desinger
this.myCustomControl.ButtonClickEvent += new System.EventHandler(this.myCustomControl_ButtonClickEvent);
myCustomControl_ButtonClickEvent(object sender, EventArgs e)
{
  // do something with the newly bubbled event
}

In case someone is still wondering how to simulate event bubbling in WinForm the method Application.AddMessageFilter is a good place to look at.

Using this method you can install your own filter which monitors all messages being posted to the current thread's message queue.

You should be aware that messages being sent (not posted) cannot be handled by this filter. Fourtounatly, most interesting events (like click events) are posted and not sent and therefore can be monitored by this filter

I would like to chime in that, as described, it actually sounds like you're chasing a red herring. While it seems like you have a situation where the lack of event bubbling in WinForms is causing you trouble, the reality is that a poor architecture is forcing you into needing event bubbling when you shouldn't.

If you can refactor/restructure your design such that the controls are working with a common data model (MVC/MVP are obvious choices) then you can simply apply common WinForms patterns like PropertyChanged events on the model to tell your main form and any other controls which consume that data to update themselves.

In short, the other answers are reasonable in that they answer the question as asked. But from a code quality standpoint, I think the better answer is to separate your data from the UI.

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