Expose and Raise Event of a Child Control in a Usercontrol in C#

expose and raise event of a child control in a usercontrol in c#

You can surface a new event and pass any subscriptions straight through to the control, if you like:

public class UserControl1 : UserControl 
{
// private Button saveButton;

public event EventHandler SaveButtonClick
{
add { saveButton.Click += value; }
remove { saveButton.Click -= value; }
}
}

Enable event visibility for a child control of an usercontrol?

I am going to suggest you are not using the UserControl as intended. These are a bit like a mini-form which consumes the events of the controls on them.

Based on Handles MyUserControl.TextBox.TextChanged it appears you want the form to get involved with text changes

Typically, these allow you to create a control made of several releated controls and encapsulate the logic to perfom some task. An example would be a UserControl to define a new Employee or Product. The UserControl would consume all the events for the different steps involved and cough up a finished object at the end. A task based example would be a Search function with several controls for filters and parameters.

What they are not, are glorified Container Controls. One reason for using a UserControl form is isolate the form from those implementation details and encapsulate them for reuse.

That said, you simply have to bubble up any events you wish to expose. Using an actual UserControl, drop your TextBox onto it:

Public Class UserControl1

Public Event MyTBTextChanged(sender As Object, e As EventArgs)

Public Sub New()
' This call is required by the designer.
InitializeComponent()

' Add any initialization after the InitializeComponent() call.

End Sub

Private Sub TextBox1_TextChanged(sender As Object,
e As EventArgs) Handles TextBox1.TextChanged
RaiseEvent MyTBTextChanged(sender, e)
End Sub
...

If you find that you are having to bubble up more than one or two events (or perhaps any), then you might want to rethink where the code logic is handled or whether a UserControl is the right choice (if lots of events for lots of controls are being exposed, why is there a UC at all?).

Exposing events of underlying control

You can forward the events like this:

    public event EventHandler SelectedIndexChanged 
{
add { inner.SelectedIndexChanged += value; }
remove { inner.SelectedIndexChanged -= value; }
}

Focus a child Control of a User Control when a combination of keys is pressed

You can probably implement IMessageFilter and handle the Keys combinations you want.

You then have to use Application.AddMessageFilter() to add the message filter and Application.RemoveMessageFilter() to remove it when not needed anymore.

Here, the UserControl's DesignMode property is checked, so the filter is added at run.time only.

Possibly, add a public Property that can add / remove / change combinations of Keys, in case there's a conflict with other controls.

The GetAncestor() function is used to determine whether the Form where the Keys combination is triggered is the Parent Form of this instance of the UserControl.

PreFilterMessage() is called when messages are generated in any Form of the application.

If you instead want to perform an action in any case, even when the combination is generated in another open Form (and, maybe, pop the Parent Form in front), just remove that check.


Filter Control + F.

If you need more filters, as mentioned, use a collection to handle these combinations.

When WM_KEYDOWN is received, WParam contains the virtual Key Code. The Virtual Key value is equivalent to the Keys enumerator.

THe ModifierKeys property contains the Key Modifiers currently active (the Control key alone is tested here, of course you can add other shortcuts that use, e.g. CTRL+SHIFT).

using System.ComponentModel;
using System.Runtime.InteropServices;

public partial class SomeUserControl : UserControl, IMessageFilter
{
public SomeUserControl() => InitializeComponent();
public bool PreFilterMessage(ref Message m) {
if (m.Msg == WM_KEYDOWN) {
if (GetAncestor(m.HWnd, GA_PARENT).Equals(ParentForm.Handle)) {
if (m.WParam.ToInt32() == (int)Keys.F && ModifierKeys == Keys.Control) {
someChildTextBox.Focus();
}
}
}
return false;
}

protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
if (!DesignMode) Application.AddMessageFilter(this);
}

protected override void OnHandleDestroyed(EventArgs e) {
if (!DesignMode) Application.RemoveMessageFilter(this);
base.OnHandleDestroyed(e);
}

private const int WM_KEYDOWN = 0x0100;
private const int GA_PARENT = 0x0002;

[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetAncestor(IntPtr hWnd, uint flags);
}

Exposing inherited events

The problem is that TextBox events do not propagate to the parent user control. If you wish them to do so, there are two ways to accomplish this:

1) Have the event on the user control ferry all additionals/removals to the event handler to the UserControl's event handler. This is roughly what your question is trying to do. However, I don't recommend it.

2) Have the event on the TextBox trigger events on the parent user control.

So, just run this code in your constructor, below InitializeComponent():

tbMain.TextChanged += (sender,e)=>OnTextChanged(e);

With this approach, a TextChanged event in the TextBox will call OnTextChanged, which raises a TextChanged event. You cannot invoke base class events directly (hence why the OnTextChanged method is provided).

Edit: tbMain.TextChanged += (sender,e)=>OnTextChanged(e); is semantically equivalent to the below code:

tbMain.TextChanged += OnTbMainTextChanged;
...
} //End of Constructor

private void OnTbMainTextChanged(object sender, EventArgs e)
{
OnTextChanged(e);
}

The benefit of using a lambda function is that it is more self-contained. Among other benefits, using a self-contained lambda function makes it obvious to future code maintainers that the sender is not being propagated, without requiring the maintainer to navigate to the named method. This is what you want: from the perspective of subscribers to the user control, your use of a TextBox to implement your control is an implementation detail.

Pass click event of child control to the parent control

While you can interact with parent form directly from child like this.ParentForm.Close(), but it's better to raise some events by child control and subscribe for the events in parent form.

Raise event from Child:

public event EventHandler CloseButtonClicked;
protected virtual void OnCloseButtonClicked(EventArgs e)
{
CloseButtonClicked?.Invoke(this, e);
}
private void CloseButton_Click(object sender, EventArgs e)
{
OnCloseButtonClicked(e);
}

Note: To raise the XXXX event, that's enough to invoke the XXXX event delegate; the reason of creating the protected virtual OnXXXX is just to follow the pattern to let the derivers override the method and customize the behavior before/after raising the event.

Subscribe and use event in Parent:

//Subscribe for event using designer or in constructor or form load
this.userControl11.CloseButtonClicked += userControl11_CloseButtonClicked;

//Close the form when you received the notification
private void userControl11_CloseButtonClicked(object sender, EventArgs e)
{
this.Close();
}

To learn more about events, take a look at:

  • Handling and raising events
  • Standard .NET event pattern

How do I make an Event in the Usercontrol and have it handled in the Main Form?

You need to create an event handler for the user control that is raised when an event from within the user control is fired. This will allow you to bubble the event up the chain so you can handle the event from the form.

When clicking Button1 on the UserControl, i'll fire Button1_Click which triggers UserControl_ButtonClick on the form:

User control:

[Browsable(true)] [Category("Action")] 
[Description("Invoked when user clicks button")]
public event EventHandler ButtonClick;

protected void Button1_Click(object sender, EventArgs e)
{
//bubble the event up to the parent
if (this.ButtonClick!= null)
this.ButtonClick(this, e);
}

Form:

UserControl1.ButtonClick += new EventHandler(UserControl_ButtonClick);

protected void UserControl_ButtonClick(object sender, EventArgs e)
{
//handle the event
}

Notes:

  • Newer Visual Studio versions suggest that instead of if (this.ButtonClick!= null) this.ButtonClick(this, e); you can use ButtonClick?.Invoke(this, e);, which does essentially the same, but is shorter.

  • The Browsable attribute makes the event visible in Visual Studio's designer (events view), Category shows it in the "Action" category, and Description provides a description for it. You can omit these attributes completely, but making it available to the designer it is much more comfortable, since VS handles it for you.

How to create event button click in usercontrol into usercontrol on winform C#

You can rig UC_Wrap with an event that forwards any events it receives from the button to whoever is subscribed to it.

partial class UC_Wrap : Control
{
public event EventHandler AddButtonClick
{
add { addButton.Click += value; }
remove { addButton.Click -= value; }
}
// etc
}

Then the UC_Control can forward those events

partial class UC_Control : Control
{
public event EventHandler AddButtonClickedInWrap
{
add { ucWrap.AddButtonClick += value; }
remove { ucWrap.AddButtonClick -= value; }
}
// etc
}

Then finally at the FormA level you can subscribe to the event and handle it.

partial class FormA : Form
{
protected override void OnLoad()
{
ucControl.AddButtonClickedInWrap += ActuallyDoSomething;
}

private void ActuallyDoSomething(object sender, EventArgs e)
{
// do something
}
}

That's probably the best way to do it. The only simpler way I can think of is to make each sub control public, but that has the major downside of exposing far more than is needed.



Related Topics



Leave a reply



Submit