I am new to .Net Winforms, i generally work on either console apps or MVC.
I am developing a MDI WinForms application, and if I make a change in one (any) window, I need all other open MDI forms to refresh when the change has been made so they can also show the updated data.
How can this be achieved - In my old Delphi (v3) days, you used to "publish" a WM_USER type message and each form would have a "subscriber handler" that would then take the required action but that was a long time ago.
All the forms are slight variations of the data and a change in one form can affect the data in the others..
TYIA
You need an event system between children and parent, which can be accomplished as follows:
Create a base class for your child forms that contains an event to be raised when any data on the form is changed. Let's call it FormChanged event.
Add an event to the Parent form to notify all children. Let's call it ChildFormChanged event.
Upon instantiation of each child form, have the parent form subscribe to the FormChanged event of the child, AND have the new child form subscribe to the ChildFormChanged event of the parent form.
The event handler for the FormChanged event in the parent form is just a pass-through function, that in turn raises the ChildFormChanged event, passing the information received from the child form causing the event to fire.
The ChildFormChanged event can be handled in the base class for child forms via a virtual event handler (to handle generic items) that can be overridden in each child class (to handle specifics of each child form).
I wrote and commented an example app in C# and posted it on Github. Here's the relevant code:
Base child Form:
public event EventHandler<EventArgs> FormChanged;
public virtual void ProcessChange(object sender, EventArgs e)
{
if((sender as Form) != this)
{
//Handle change
}
}
protected void NotifyParent() => FormChanged?.Invoke(this, EventArgs.Empty);
Parent form:
public event EventHandler ChildFormChanged;
public void NotifyAllChildren(object sender, EventArgs e)
=> ChildFormChanged?.Invoke(sender, e);
//Child form creation function
private void createNewFormToolStripMenuItem_Click(object sender, EventArgs e)
{
MDIChildBase newChild = new MDIChild(); //Can be different child forms
newChild.MdiParent = this;
//Parent-child event subscription
newChild.FormChanged += NotifyAllChildren;
ChildFormChanged += newChild.ProcessChange;
newChild.Show();
}
Every child form must call base.NotifyParent(); once any changes occur that you want to propagate to other child forms.
You can still use the windows messaging infrastructure. Explained here and here.
Related
I have a Windows form, having a pane, which contains another class, derived from Windows Forms. This is contained as a control within the pane. It contains two buttons within itself.
I'd like the events of the child control to be passed all the way to the parent window. For example, the child window in the pane has a Cancel button, which is supposed to close it. I'd like the parent control, i.e., the main window to close as well, but how can I intercept the button click event of the child control?
I can modify the child control, but only if there is no other way to achieve this in a proper way, I'd rather like to avoid it.
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
I have a Windows form, having a pane, which contains another class, derived from Windows Forms. This is contained as a control within the pane. It contains two buttons within itself.
I'd like the events of the child control to be passed all the way to the parent window. For example, the child window in the pane has a Cancel button, which is supposed to close it. I'd like the parent control, i.e., the main window to close as well, but how can I intercept the button click event of the child control?
I can modify the child control, but only if there is no other way to achieve this in a proper way, I'd rather like to avoid it.
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
This must have been asked before, but I am fairly new and don't quite know how to express myself...
1) I have a UserControl that basically acts as a toolbar. I re-use the toolbar in each window, hence the need for a uc.
2) The toolbar is filled with buttons
3) the usercontrol doesn't act on the button (no code), but it should pass the event back to the parent window so the code in the parent window fires up.
How can I do this? Is this a routed event? any sample code in vb.net would be appreciated!
On you user control, you need events that you can fire when the buttons are clicked. Then in your form, you handle the events just like you do for every other control. IE:
public event Button1_ClickedEventHandler Button1_Clicked;
public delegate void Button1_ClickedEventHandler(object sender);
private void Button1_Click(object sender, EventArgs e)
{
if (Button1_Clicked != null) {
Button1_Clicked(this);
}
}
You can call the event whatever you want and pass whatever you want. Here you will notice I am NOT sending the button but THIS, which in this case should be the User Control.
I am currently developing a Windows app with several forms. I use Form_Shown in one of those forms to execute some code to initialize (refresh) the form before showing it.
In Form.Shown Event on MSDN, it states that the event is raised only the first time the form is shown. However, I want to be able to execute code to initialize my form every time that I call Form.Show() in some of the forms. Here's an example.
From a form named Game. Contains an event handler Game_Shown and a button that when clicked shows a form named Menu:
private void btnMenu_Click(object sender, EventArgs e)
{
this.Hide();
Formulaires.formMenu.Show();
}
private void Game_Shown(object sender, EventArgs e)
{
Code here...
this.Refresh();
}
From the form named Menu. Contains a button that when clicked shows the form named Game:
private void lblGame_Click(object sender, EventArgs e)
{
this.Hide();
Formulaires.formGame.Show();
}
It is behaving by design.
From the docs:
The Shown event occurs whenever the form is first shown.
Also, you should not handle the Shown event in your class, rather you should override OnShown.
To achieve what you want, you might try overriding the OnVisibleChanged method. Inside the method, if the form is visible, then execute your code.
Like the Shown event, you should not handle it in your form class, instead override the appropriate method:
From the docs:
The OnVisibleChanged method also allows derived classes to handle the event without attaching a delegate. This is the preferred technique for handling the event in a derived class.
What you want requires some detailed knowledge about which event happens when in the WinForm lifecycle. That may be documented somewhere, I don't know.
This is how I would find out:
create a small test project with 2 forms (Main and helper)
add the show and hide buttons and make sure it works.
Add Debug.Print("EventName") to all the candidate events of the helper form.
Look at the log in the output window and pick your event.
Candidate events would be FormClosing, FormClosed, (De)Activated, Enter, Leave, Load, ... go through the list.
When you find the right one, please post it here in an answer.
I'm developing a Windows Mobile 5.0 or above application with .Net Compact Framework 2.0 SP2 and C#.
I have a Winform (Form1) with a control (Control1) that contains another control (Control2). For example, a winform with a panel and inside this panel there is a button, but in my case Control1 and Control2 are custom controls.
Control2 has an event, Click, that is thrown when the user does click over it. This click event must be handled by Form1. To do it, first I handle the event on Control1 that throws a new event that is handled on Form1. This my code:
On Control1:
public event EventHandler Control2Click;
private void control2_Click(object sender, EventArgs e)
{
if (Control2Click != null)
{
Control2Click(sender, e);
}
}
On Form1:
private void control1_Control2Click(object sender, EventArgs e)
{
// Do something interesting.
}
Is there a better way to handle Control2_Click directly in Form1? I don't know if my way has a bad performance and this kind of events can be handled better.
Thank you!
No, you are doing it right. It is the correct way the bubble an event out of a nested control that isn't directly accessible from a container control. You'd normally use the PerformClick() method to fire the Click event but this doesn't appear to be available in CF.
Perf is not an issue, calling a delegate target is very fast, a dozen nanoseconds or so on a desktop machine. Click is a "human-time" event, anything less than 20 milliseconds is perceived as "instant".
What's stopping you from hooking up the Control2 Click event directly from Form1? Does Control1 expose Control2 via a property? Or perhaps expose an event on Control1 which actually hooks up to the Control2 Click event? For example:
// In Control1
// Assuming Control2 is some sort of Save button, for example
public EventHandler SaveClicked
{
add { control2.Click += value; }
remove { control2.Click -= value; }
}
Note that:
If you change the value of control2 within Control1, the event handlers won't be "transferred" which would be be unfortunate
The sender parameter in the event handler will refer to Control2, not Control1
To be honest I wouldn't expect this to be a performance problem however you handle it - it's just a delegate invocation or two.