c# execute a method from Form in the MainForm [duplicate] - c#

I am working with windowsFrom in c#. I am trying to call mainfrom method in one of the from in user control.
I have mainfrom like this
namespace Project
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
public void TempCommand()
{
StartTemp();
}
}
}
I have the button click in the user control. When i click that button then it will open another form. I have the code like this in the user control.
private TempCalib _tempCalib = new TempCalib();
private void calibBtn_Click(object sender, EventArgs e)
{
_tempCalib.Show();
}
it will open another from and i have one button in that from. I need to call mainfrom method when i click "Ok" button in this from.
namespace Project
{
public partial class TempCalib : Form
{
public TempCalib()
{
InitializeComponent();
}
private void OkButton_Click(object sender, EventArgs e)
{
// I need to call the mainfrom "TempCommand" method here.
this.Hide();
}
}
}
Can anyone help me how to do this.
Thanks.

Quick answer
Just add a reference to the primary form in your secondary form:
public partial class TempCalib : Form
{
private MainForm _main
public TempCalib(MainForm main) : this()
{
_main = main;
}
/// Other stuffs
}
Then assign value when you construct your secondary form:
private TempCalib _tempCalib;
private void calibBtn_Click(object sender, EventArgs e)
{
if (_tempCalib == null)
_tempCalib = new TempCalib(this);
_tempCalib.Show();
}
If calibBtn_Click isn't inside MainForm (but it's inside a UserControl on it) then you can replace _tempCalib initialization with:
_tempCalib = new TempCalib((MainWindow)FindForm());
You'll be then able to call the primary form:
private void OkButton_Click(object sender, EventArgs e)
{
_main.TempCommand();
this.Hide();
}
Notes: this is just one option, you may create a property to hold MainForm reference (so secondary form can be reused and it'll be more designer friendly) moreover TempCalib is not an UserControl but a Form (pretty raw but for an UserControl you may just check its parent Form and cast it to proper type).
Improvements
Such kind of references are often an alert. Usually UI components shouldn't not be so coupled and a public Form's method to perform something very often is the signal that you have too much logic in your Form. How to improve this?
1. DECOUPLE CONTROLS. Well a first step may be to decouple them a little bit, just add an event in TempCalib and make MainForm its receiver:
public partial class TempCalib : Form
{
public event EventHandler SomethingMustBeDone;
private void OkButton_Click(object sender, EventArgs e)
{
OnSomethingMustBeDone(EventArgs.Empty); / TO DO
this.Hide();
}
}
Then in MainForm:
private TempCalib _tempCalib;
private void calibBtn_Click(object sender, EventArgs e)
{
if (_tempCalib == null)
{
_tempCalib = new TempCalib();
_tempCalib.SomethingMustBeDone += _tempCalib_SomethingMustBeDone;
// In _tempCalib_SomethingMustBeDone you'll invoke proper member
// and possibly hide _tempCalib (remove it from OkButton_Click)
}
_tempCalib.Show();
}
2. DECOUPLE LOGIC FROM CONTROLS. UI changes pretty often, logic not (and when it changes probably isn't in parallel with UI). This is just the first step (now TempCalib isn't aware of who will use it). Next step (to be performed when too much things happen inside your form) is to remove this kind of logic from the form itself. Little example (very raw), keep TempCalib as before (with the event) and change MainForm to be passive:
public partial class MainForm : Form
{
public event EventHandler Calibrate;
protected virtual void OnCalibrate(EventArgs e)
{
// TODO
}
}
Now let's create a class to control the flow and logic:
public class MyTaskController
{
private MainForm _main;
private TempCalib _tempCalib;
public void Start()
{
_main = new MainForm();
_main.Calibrate += OnCalibrationRequested;
_main.Show(); // Or whatever else
}
private void OnCalibrationRequested(object sender, EventArgs e)
{
if (_tempCalib == null)
{
_tempCalib = new TempCalib();
_tempCalib.SomethingMustBeDone += OnSomethingMustBeDone();
}
_tempCalib.Show();
}
private OnSomethingMustBeDone(object sender, EventArgs e)
{
// Perform the task here then hide calibration window
_tempCalib.Hide();
}
}
Yes, you'll need to write much more code but this will decouple logic (what to do as response to an action, for example) from UI itself. When program grows up this will help you to change UI as needed keeping logic unaware of that (and in one well defined place). I don't even mention that this will allow you to use different resources (people) to write logic and UI (or to reuse logic for different UI, WinForms and WPF, for example). Anyway IMO the most obvious and well repaid benefit is...readability: you'll always know where logic is and where UI management is, no search, no confusion, no mistakes.
3. DECOUPLE LOGIC FROM IMPLEMENTATION. Again you have more steps to perform (when needed). Your controller is still aware of concrete types (MainForm and TempCalib). In case you need to select a different form at run-time (for example to have a complex interface and a simplified one or to use dependency injection) then you have to decouple controller using interfaces. Just an example:
public interface IUiWindow
{
void Show();
void Hide();
}
public interface IMainWindow : IUiWindow
{
event EventHandler Calibrate;
}
public interface ICalibrationWindow : IUiWindow
{
event EventHandler SomethingMustBeDone;
}

You could use a custom event that is declared in your UserControl. Then your form needs to handle this event and call the method you want to call. If you let the UserControl access your form, you are hard-linking both with each other which decreases reusability of your UserControl.
For example, in TempCalib:
public delegate void OkClickedHandler(object sender, EventArgs e);
public event OkClickedHandler OkClicked;
private void OkButton_Click(object sender, EventArgs e)
{
// Make sure someone is listening to event
if (OkClicked == null) return;
OkClicked(sender, e);
this.Hide();
}
in your mainform:
private void Mainform_Load(object sender, EventArgs e)
{
_tempCalib.OkClicked += CalibOkClicked;
}
private void CalibOkClicked(Object sender, EventArgs e)
{
StartTemp();
}

You create an event in your usercontrol and subscribe to this in the mainform.
That is the usual way.

Form1 Code:
UserControl1 myusercontrol = new UserControl1();
public void TabClose(Object sender,EventArgs e)
{
int i = 0;
i = tabControl1.SelectedIndex;
tabControl1.TabPages.RemoveAt(i);
}
private void Form1_Load(object sender, EventArgs e)
{
myusercontrol.Dock = DockStyle.Fill;
TabPage myTabPage = new TabPage();
myTabPage.Text = "Student";
myTabPage.Controls.Add(myusercontrol);
tabControl1.TabPages.Add(myTabPage);
myusercontrol.OkClick += TabClose;
}
UserControl1 Code:
public delegate void OkClickedHandler(Object sender, EventArgs e);
public partial class UserControl1 : UserControl
{
public event OkClickedHandler OkClick;
public UserControl1()
{
InitializeComponent();
}
private void button3_Click(object sender, EventArgs e)
{
if (OkClick == null) return;
OkClick(sender, e);
}
}

Try this:
From user control try this:
MainForm form = this.TopLevelControl as MainForm;
form.TempCommand();

Related

"Object reference not set..." exception when adding control to another form

I'd like to add a UserControl (UserControl2) to a panel on another form (MainForm) when the user clicks a button on the UserControl that is currently in the panel (UserControl1). UserControl2 should replace UserControl1 as the contents of the panel when this button is clicked.
I've tried figuring out how to use an event to communicate between UserControl1 and MainForm, but I just don't know where to start, as I can't find an example that is easily adaptable to my particular situation (Adding a control to a panel).
THIS question is similar, but doesn't quite fit what I'm trying to do (or at least I just don't know how to adapt it to what I'm trying to do)
I've tried this method, and get no errors, but my button doesn't do anything:
MainForm:
private void SubscribeToEvent(MyUserControl1 Button_Click)
{
MyUserControl1 CurrentClass = new MyUserControl1();
CurrentClass.Button.Click += new EventHandler(this.ButtonClickHandler);
}
private void ButtonClickHandler(object sender, EventArgs e)
{
MyUserControl2 MainPanelControls = new MyUserControl2();
MainPanel.SuspendLayout();
MainPanel.Controls.Clear();
MainPanel.Controls.Add(MainPanelControls);
MainPanel.ResumeLayout();
}
UserControl1:
public void Button_Click(object sender, EventArgs e)
{
//... Not sure what I'm missing here
}
I've also tried this method, this time trying to implement something similar to the method described in the link near the top of my question. I know these are obviously wrong (otherwise I wouldn't need to ask the question), but I don't have enough knowledge on the subject to figure it out on my own:
UserControl1:
public MyUserControl1()
{
InitializeComponent();
}
private MainForm mainForm = null;
public MyUserControl1(Form callingForm)
{
mainForm = callingForm as MainForm;
InitializeComponent();
}
//...
private void Button_Click(object sender, EventArgs e)
{
MyUserControl2 MainPanelControls = new MyUserControl2();
mainForm.MainPanel.SuspendLayout();
mainForm.MainPanel.Controls.Clear();
mainForm.MainPanel.Controls.Add(MainPanelControls);
mainForm.MainPanel.ResumeLayout();
}
Now when I click Button I get an "Object reference not set to an instance of an object" error at mainForm.MainPanel.SuspendLayout(); (Or anything past that point).
I've also tried modifying Joh's answer, but end up with the same Null Reference Exception, this time on my Button_Click event at ButtonClickedToMainForm(this, e); I'm not sure if I need to create an instance of ButtonClickedToMainForm, or how to do that properly (if it's not something else, that is).
I have a feeling I've likely just placed some of this code in the wrong place, so I'm hoping someone more experienced may be able to help me sort that out.
UPDATE
This is my attempt at implementing Joh's answer, being so new to this, I'm not quite sure where I've messed up:
MainForm:
namespace MyProject
{
public delegate void ButtonClickToMainForm(object sender, EventArgs e);
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
UserControl1 userControl1 = new UserControl1();
userControl1.Button1Clicked += userControl1_Button1Clicked;
}
private void userControl1_Button1Clicked(object sender, EventArgs e)
{
try
{
UserControl2 MainPanelControls = new UserControl2();
MainPanel.SuspendLayout();
MainPanel.Controls.Clear();
MainPanel.Controls.Add(MainPanelControls);
MainPanel.ResumeLayout();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
UserControl1:
public partial class UserControl1: UserControl
{
public event ButtonClickToMainForm Button1Clicked;
public UserControl1()
{
InitializeComponent();
}
private void OnButton1Clicked(object sender, EventArgs e)
{
Button1Clicked?.Invoke(this, e);
}
private void Button1_Click(object sender, EventArgs e)
{
Button1Clicked(this, e); //"Object reference not set to an instance of an object."
}
}
I'm sure it's something simple I'm missing on UserControl1, I'm just not sure what.
Here is an easy example for a delegate event.When you implement it in all your usercontrols it should work for your problem.
UserControl:
//Create a delegate
public delegate void ButtonClickToMainForm(object sender, EventArgs e);
public partial class UserControl1 : UserControl
{
//Your own event based on created delegate
public event ButtonClickToMainForm ButtonClickedToMainForm;
public UserControl1()
{
InitializeComponent();
}
//This method will invoke your event
private void OnButtonClickedToMainForm(object sender, EventArgs e)
{
ButtonClickedToMainForm?.Invoke(this, e);
}
private void button1_Click(object sender, EventArgs e)
{
//On button1_Click this will fire the event on mainform
OnButtonClickedToMainForm(this, e);
}
and the mainForm :
public Form1()
{
InitializeComponent();
//Subscribe to event from your usercontrol
userControl11.ButtonClickedToMainForm += UserControl11_ButtonClickedToMainForm;
}
//Button on userControl1 has been clicked
private void UserControl11_ButtonClickedToMainForm(object sender, EventArgs e)
{
//do your things here...
}
Hope this helps.

Call public parent function from child class

Inside my Main method I'm instantiating the UpdateDialog class inside which based on if the user presses a button or not I need to call function1() from Main. Here is the code:
public partial class Main : Form
{
public void function1()
{
doing_stuff_here();
}
private void button1_Click(Object sender, EventArgs e)
{
var update = new UpdateDialog();
update.ShowDialog();
}
}
public partial class UpdateDialog : Form
{
private void button2_Click(object sender, EventArgs e)
{
//call here function1() from Main
}
}
What should I do to be able to call function1() from Main inside the partial class UpdateDialog?
LE: although the method suggested by Styxxy seems right it doesn't work well in my app because of cross-thread invalid operation so I ended up using the delegate workaround suggested by Cuong Le.
You'll have to have an instance of the Main form in your UpdateDialog form. As you say that UpdateDialog is a child form of your Main form, I guess that you create the UpdateDialog in your Main form and do a show there. Before showing that form, you could assign the Parent property.
var updateDialog = new UpdateDialog();
// Or use "UpdateDialog updateDialog = new UpdateDialog();" as people like Andreas Johansson don't like the "var" keyword
// Do other stuff here as well
updateDialog.Parent = this;
// Or use Show() for non modal window
updateDialog.ShowDialog();
You get the error ArgumentException: Top-level control cannot be added to a control.. Now this can be solved in two ways.
You can set the TopLevel property to false on your Main form (I'm not a huge fan of this).
You can use the Owner property to your Main form (this). Below two ways of doing it.
You can set the Owner manually:
updateDialog.Owner = this;
Or you can add this as parameter to the Show(owner) or ShowDialog(owner) methods; this way, the Owner is also being set.
updateDialog.Show(this);
// or
updateDialog.ShowDialog(this);
"Full" code makes this:
var updateDialog = new UpdateDialog();
// Do other stuff here as well
updateDialog.Owner= this;
updateDialog.ShowDialog(); // or use .Show()
// or
updateDialog.ShowDialog(this); // or use .Show(this)
I suggest you create an event in UpdateDialog then subscribe it after you create an instance inside the Main class. This way you have a better separation between these 2 classes.
public partial class Main
{
public void function1()
{
doing_stuff_here();
}
private void button1_Click(object sender, EventArgs e)
{
var update = new UpdateDialog();
update.OnButton2Click += OnUpdateDialogButton2Click;
update.ShowDialog();
}
void OnUpdateDialogButton2Click(object sender, EventArgs e)
{
function1();
}
}
public partial class UpdateDialog
{
public event EventHandler<EventArgs> OnButton2Click;
private void button2_Click(object sender, EventArgs e)
{
//call here function1() from Main
if (OnButton2Click != null)
{
this.OnButton2Click(this, e);
}
}
}
Pass Main class instance to your Update Form and store it in instance variable -
Main mainWindow = null;
public UpdateDialog(Main mainForm)
{
mainWindow = mainForm;
}
private void button2_Click(object sender, EventArgs e)
{
mainWindow.function1();
}
And from Main method -
private void button1_Click(Object sender, EventArgs e)
{
var update = new UpdateDialog(this);
update.ShowDialog();
}
You could turn it around, and let Main form listen to clicks from the UpdateDialog.
In Main:
private void button1_Click(Object sender, EventArgs e)
{
var update = new UpdateDialog();
update.OnSomethingClicked += function1;
update.ShowDialog();
}
void form_OnSomethingHappened(object sender, EventArgs e)
{
// Do the stuff you want
}
In UpdateDialog:
public event EventHandler OnSomethingHappened;
private void button2_Click(object sender, EventArgs e)
{
EventHandler handler = OnSomethingHappened;
if (handler != null) handler(this, e);
}
The ShowDialog() method returns a DialogResult you can call function1 one of after the dialog is closed.
http://msdn.microsoft.com/en-us/library/c7ykbedk.aspx
Can pass the Main class like a reference.
For example:
public partial class Main : Form
{
//make it internal, if UpdateDialog in the same assembly, and it only one that would use it. In other words hide it for outside world.
internal void function1()
{
doing_stuff_here();
}
....
}
public partial class UpdateDialog : Form
{
private MainForm _main = null;
public UpdateDialog (MainForm main) { //Accept only MainForm type, _not_ just a Form
_main = main;
}
private void button2_Click(object sender, EventArgs e)
{
_main.function1(); //CALL
}
}
Something like this. You can change this accroding to your precise requirements, but this is a general idea.
Approach #1
You need to create an instance of class Main.
Main foo = new Main();
foo.function1();
Approach #2
You need to declare the method as static.
public static function1(){ ... }
....
Main.function1();
You can make your function1 a Partial method and this way you can use it in all your partial classes.
Partial methods allow the definition of a method to be located in one file and the body of the method to be optionally defined in another file. They can only be used in partial classes and were introduced as language features in C# 3.0 and Visual Basic 9.0, the versions that shipped with the .NET Framework 3.5 and Visual Studio 2008.
So what you can do is modify like this
public partial class Main : Form
{
public partial void function1()
{
doing_stuff_here();
}
private void button1_Click(object sender, EventArgs e)
{
var update = new UpdateDialog();
update.ShowDialog();
}
}
public partial class UpdateDialog : Form
{
public partial void function1();
private void button2_Click(object sender, EventArgs e)
{
function1();
}
}

How to reference a form from subform?

Well here is the thing, I have MainForm that call OrderForm.Show().
Now I want to call public function in MainForm with a button, but I cant.
Here is the code in MainForm:
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
// Main form
private void MainFrm_Load(object sender, EventArgs e)
{
FormOrder frmO = new FormOrder();
frmO.Show();
}
public void Refresh()
{
// some action
}
}
And in OrderForm I do this:
public partial class FormOrder : Form
{
public FormOrder()
{
InitializeComponent();
}
private void ShowForm()
{
// some action
}
private void btnCopy_Click(object sender, EventArgs e)
{
Form form = Form.ActiveForm as frmMain;
if (form != null)
{
form.Refresh();
}
}
}
so in the Program I run MainForm.Show() then load OrderForm. Then I when I click Copy button, it will run Public Function Refresh in MainForm. But I cant make it work, it always return null in:
Form form = Form.ActiveForm as frmMain;
so how can I actually get the active form, is it another solution? or I get it wrong??
Thanks in advance for the answer :)
You're getting a null because your active form is the one you're in and it is not of type frmMain. (I think you may actually be confusing an active form for a parent form?)
There are so may ways to do this. You could make your frmMain a singleton but that's weird and ugly and not recommended of late or you could pass a reference of it to its children somehow. Here's one, simple way:
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
// Main form
private void MainFrm_Load(object sender, EventArgs e)
{
FormOrder frmO = new FormOrder(this); // pass a ref of self
frmO.Show();
}
public void Refresh()
{
// some action
}
}
And in OrderForm:
public partial class FormOrder : Form
{
private frmMain _parent; // create a field that refers to the parent
public FormOrder(frmMain parent) // mod the constructor
{
if (parent == null) throw new NullReferenceException("Can't be NULL!!!"); // check clause
_parent = parent; // assign the ref of the parent
InitializeComponent();
}
private void ShowForm()
{
// some action
}
private void btnCopy_Click(object sender, EventArgs e)
{
_parent.Refresh(); // make the call to parent
}
}
You could make this better by using an interface but the general idea would be the same.
I would not suggest you take the route to access the active form to accomplish your target. What you need to do is to pass a reference of the MainForm to the OrderForm, so OrderForm could use the reference to call any method on the MainForm.
private void MainFrm_Load(object sender, EventArgs e)
{
FormOrder frmO = new FormOrder();
frmO.MainForm = this;
frmO.Show();
}
And in FormOrder class you should add a new attribute MainForm, and use the attribute to reference the calling form :-
public partial class FormOrder : Form
{
public Form MainForm;
public FormOrder()
{
InitializeComponent();
}
private void ShowForm()
{
// some action
}
private void btnCopy_Click(object sender, EventArgs e)
{
if (MainForm != null)
{
MainForm.Refresh();
}
}
}
You need to have a reference to the mainForm and/or mainForm's button click event that lives outside of mainForm. You can do this is in various ways. Create a static class that has a reference to mainForm, create a static class that has a delegate to the mainForm click event, pass mainForm into the contructor for childForm, pass in a delegate to mainForms click event to the the constructor for the childForm. Create a static method that handles the click event then use that from within childForm.
It really all depends upon what you want to to do, what you need to do, and how you desire to do it.
While the other answers are absolutely correct, I'd like to suggest a much easier yet "dirty" way to do it.
Every component has a Tag property where you can store any user-defined value or reference in. The idea is to save a reference to the main form in the second form's Tag, and access it from the button click event handler.
The good thing is that you do not have to write a lot of code, or create any data structure.
The dirty thing about it is that neither the framework at runtime nor the compiler at compile-time have any idea what you store in .Tag, and since it is not typed correctly, you are doing a type cast, depending on you storing the correct value there beforehand.
If you store the wrong value there, you might have a hard time debugging the cause since the symptom does not signal where you put the wrong value into Tag. So wild Tag usuage in a team of programmers is a good thing if you want to give birth to many unexpected hard-to-detect-and-fix bugs :)
Also, there is only one Tag per component. If you need more than one user-defined value, you might end up creating a custom data structure which Tag then holds a reference to, but that probably is a bigger mess than all of the other suggestions.
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
// Main form
private void MainFrm_Load(object sender, EventArgs e)
{
FormOrder frmO = new FormOrder();
frmO.Tag=this;
frmO.Show();
}
public void Refresh()
{
// some action
}
}
public partial class FormOrder : Form
{
public FormOrder()
{
InitializeComponent();
}
private void ShowForm()
{
// some action
}
private void btnCopy_Click(object sender, EventArgs e)
{
Form form = Tag as frmMain; // form now references the main form
if (form != null)
{
form.Refresh();
}
}
}
A good example for a quick and dirty solution, with limitations so inherent that you usually identify them only when it is too late -- that's why I am telling in the first place ;)

call event from form2 in form1

hi
call event from form2 in form1?
for example :
The following code into form2 :
private void Form2_Load(object sender, EventArgs e)
{
MessageBox.Show("http://stackoverflow.com");
}
What to write in a form1?
Why are you wanting to call the event? Will you know the sender and the Event Args?
Why don't you just create a public method in Form2 that Form1 is able to see?
how about form2.Form2_Load(this, null)
You can't call private members of a class from outside it.
You can change the accessibility to internal, which will make it visible within the assembly - if your form1 is in the same assembly.
Alternatively you can make it a public method, which would make it globally accessible.
However, you shouldn't call event handlers in such a manner - they are supposed to handle events that the declaring class raises.
For the sample code you gave, a better solution would be to create a public or internal method that can be called from this event handler:
private void Form2_Load(object sender, EventArgs e)
{
MyMethod();
}
public MyMethod()
{
MessageBox.Show("http://stackoverflow.com");
}
In order to call this method from form1, it needs to know about form2:
// in form1
Form frm2 = new Form2();
frm2.MyMethod();
You can't raise an Event from outside a class.
The convention is that you call a OnEventname method in the class. Usually this method is protected (can't only accessed from the class itself or others that inherit from it)
// in form1
private void Method1()
{
using (var form2 = new Form2())
{
form2.Show();
form2.RaiseLoadEvent(EventArgs.Empty);
}
}
// Create this method in form2
public void RaiseLoadEvent(EventArgs e)
{
OnLoad(this, e);
}
// The OnLoad method already exists in form 2
// But it usually looks like this
protected void OnLoad(EventArgs e)
{
var eh = LoadEventHandler;
if (eh != null)
{
eh(this, e);
}
}
But I don't suggest to raise the LoadEvent, because It is raised only once after the creation of the form. More usual is to react to the Load event to modify the form.
privat void Method1()
{
using (var form2 = new Form2())
{
// Add Event Handler
form2.Load += new EventHandler(form2_Load);
form2.ShowDialog();
}
// Allways remove Event Handler to avoid memory leaks
form2.Load -= new EventHandler(form2_Load);
}
private void form2_Load(object sender, EventArgs e)
{
form2.Text = "Hello from form1";
}
Form1 (the event publisher) should expose a separate, public event property for Form2 (the subscriber) to subscribe to.
For example: the form publishing the event will look like this:
public partial class Publisher : Form
{
public event PostUpdateHandler OnPostUpdate;
public Publisher()
{
InitializeComponent();
new Subscriber(this).Show();
}
private void button1_Click(object sender, EventArgs e)
{
if (OnPostUpdate != null)
{
OnPostUpdate(new PostUpdateArgs(textBox1.Text));
}
}
}
public delegate void PostUpdateHandler(PostUpdateArgs args);
public class PostUpdateArgs : EventArgs
{
public string UpdateText;
public PostUpdateArgs(string s)
{
UpdateText = s;
}
}
The subscribing form looks like this:
public partial class Subscriber : Form
{
public Subscriber()
{
InitializeComponent();
}
public Subscriber(Publisher publisher) : this()
{
publisher.OnPostUpdate += new PostUpdateHandler(publisher_OnPostUpdate);
}
private void publisher_OnPostUpdate(PostUpdateArgs args)
{
this.Form2_Load(null, null);
}
private void Subscriber_FormClosed(object sender, FormClosedEventArgs e)
{
this.Dispose();
}
private void Form2_Load(object sender, EventArgs e)
{
MessageBox.Show("http://stackoverflow.com");
}
}
When the user presses button1 on the publishing form, the subscribing form will execute the code associated with the delegate, resulting in a message box popping up with the message http://stackoverflow.com.

Listening to Events in Main Form from Another Form in C#

I have an application that has a main form and uses an event handler to process incoming data and reflect the changes in various controls on the main form. This works fine.
I also have another form in the application. There can be multiple instances of this second form running at any given time.
What I'd like to do is have each instance of this second form listen to the event handler in the main form and update controls on its instance of the second form.
How would I do this?
Here's some sample code. I want to information from the_timer_Tick event handler to update each instance of SecondaryForm.
public partial class Form1 : Form
{
Timer the_timer = new Timer();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
the_timer.Tick += new EventHandler(the_timer_Tick);
the_timer.Interval = 2000;
the_timer.Enabled = true;
}
void the_timer_Tick(object sender, EventArgs e)
{
// I would like code in here to update all instances of SecondaryForm
// that happen to be open now.
MessageBox.Show("Timer ticked");
}
private void stop_timer_button_Click(object sender, EventArgs e)
{
the_timer.Enabled = false;
}
private void start_form_button_Click(object sender, EventArgs e)
{
SecondaryForm new_form = new SecondaryForm();
new_form.Show();
}
}
class SecondForm
{
private FirstForm firstForm;
public SecondForm()
{
InitializeComponent();
// this means unregistering on form closing, uncomment if is necessary (anonymous delegate)
//this.Form_Closing += delegate { firstForm.SomeEvent -= SecondForm_SomeMethod; };
}
public SecondaryForm(FirstForm form) : this()
{
this.firstForm = form;
firstForm.Timer.Tick += new EventHandler(Timer_Tick);
}
// make it public in case of external event handlers registration
private void Timer_Tick(object sender, EventArgs e)
{
// now you can access firstForm or it's timer here
}
}
class FirstForm
{
public Timer Timer
{
get
{
return this.the_timerl
}
}
public FirstForm()
{
InitializeComponent();
}
private void Button_Click(object sender, EventArgs e)
{
new SecondForm(this).ShowDialog(); // in case of internal event handlers registration (in constructor)
// or
SecondForm secondForm = new SecondForm(this);
the_timer.Tick += new EventHandler(secondForm.Timer_tick); // that method must be public
}
Consider using loosely coupled events. This will allow you to couple the classes in such a way that they never have to be directly aware of each other. The Unity application block comes with an extension called EventBroker that makes this very simple.
Here's a little lick of the sugar:
public static class EVENTS
{
public const string UPDATE_TICKED = "event://Form1/Ticked";
}
public partial class Form1 : Form
{
[Publishes(EVENTS.UPDATE_TICKED)]
public event EventHandler Ticked;
void the_timer_Tick(object sender, EventArgs e)
{
// I would like code in here to update all instances of SecondaryForm
// that happen to be open now.
MessageBox.Show("Timer ticked");
OnTicked();
}
protected virtual void OnTicked()
{
if (Ticked == null) return;
Ticked(this, e);
}
}
public partial class SecondaryForm : Form
{
[SubscribesTo(EVENTS.UPDATE_TICKED)]
private void Form1_Ticked(object sender, EventHandler e)
{
// code to handle tick in SecondaryForm
}
}
Now if you construct both of these classes using Unity, they will automatically be wired together.
Update
Newer solutions use message bus to handle loosely coupled events. See http://masstransit-project.com/ or http://nimbusapi.github.io/ as examples.
I guess you can make SecondaryForm take in the parent form in the constructor, and the add an event handler in the constructor.
private void start_form_button_Click(object sender, EventArgs e)
{
SecondaryForm new_form = new SecondaryForm(this);
new_form.Show();
}
In SecondaryForm.cs:
public SecondaryForm(ISomeView parentView)
{
parentView.SomeEvent += .....
}

Categories