I have a winform application that goes with name myForm. Within this form I open another form:
private OtherForm otherForm; //this is a field
private OpenOtherForm()
{
if (otherForm == null)
{
otherForm = new OtherForm();
otherForm.FormClosing += delegate { MessageBox.Show("OtherForm will be closed"); otherForm = null};
}
MessageBox.Show("Form is already active!");
}
This works fine. But I have some methods in the second form as well. I would like to try capturing theire call.
For example if OtherForm.DoSomething() is called within the second form, I want a message box to show thi.
I tried to assign OtherForm.DoSomething() += delegate { /* mesagebox */}; But this does not compile
otherForm.FormClosing += delegate { .. } is compiling because FormClosing is of type Event. An event can be subscribed to and when it's fired your code will run.
You can't use this syntax on a method like DoSomething(). A method can only be called with something like otherForm.DoSomething(). The code in DoSomething() will then be executed.
What you can do however is create your own event and fire it when DoSomething() is executed in the second form.
Here is the MSDN Documentation on publishing your own Event.
It would be something like:
public event EventHandler RaiseCustomEvent;
public void DoSomething()
{
OnRaiseCustomEvent();
}
protected virtual void OnRaiseCustomEvent()
{
EventHandler handler = RaiseCustomEvent;
if (handler != null)
{
handler(this, EventArgs.Empty););
}
}
If you want to respond to a call in another form you could add an event to the other form and raise it in the method you are trying to respond to.
class Form1: Form
{
public void Button1_Click(object sender, EventArgs e)
{
var form2 = new Form2();
form2.SomeMethodCalled += Form2_SomeMethodCalled;
}
public void Form2_SomeMethodCalled(object sender, EventArgs e)
{
// method in form2 called
}
}
class Form2 : Form
{
public event EventHandler SomeMethodCalled;
public void SomeMethod()
{
OnSomeMethodCalled();
// .....
}
private void OnSomeMethodCalled()
{
var s = SomeMethodCalled;
if(s != null)
{
s(this, EventArgs.Empty);
}
}
}
Related
I'm quite new in C#, so I'm struggling with this more than two days. I hope that some one can help me out with this one.
Below some simplified code from my application.
I want to pass a List from Form1 to Form2 using delegate and event.
How can I do this? I read tons of explanations about events and delegates, but I still can't figure it out, how this really works.
Form1:
public delegate List<string> ProfileImportEventHandler();
public event ProfileImportEventHandler ProfileImported;
private void btnImport_Click(object sender, EventArgs e)
{
// raise an event
OnProfileImported();
}
protected virtual void OnProfileImported()
{
if (ProfileImported != null) // check if there are subscribers
{
ProfileImported();
}
}
Form2:
public partial class Form2 : Form
{
Form1 frm1;
public Form1()
{
// Constructor logic
frm1.ProfileChanged += new Form1.ProfileImportEventHandler(Form1_OnProfileImported);
}
}
List<string> Form1_OnProfileImported()
{
// TO DO
}
UPDATE
None of the solutions worked so far. Here is what I have already tried:
Form 2
// use generic list for profiles that will be imported from USB-Stick
private List<string> profilePaths = new List<string>();
public delegate void ProfileImportEventHandler(object sender, ProfileImportEventArgs e);
public event ProfileImportEventHandler ProfileImported;
public delegate void ImportButtonClickedEventHandler();
public event ImportButtonClickedEventHandler ButtonImportClicked;
public delegate void HaveDataDelegate(IList<string> data);
public event HaveDataDelegate HaveData;
//....
private void btnImport_Click(object sender, EventArgs e)
{
// do something...
// raise an event
var ea = new ProfileImportEventArgs(profilePaths);
OnProfileImported(ea);
OnButtonImportClicked();
// When there is data:
var copy = HaveData; // Use copy to avoid race conditions
if (copy != null)
{
copy(profilePaths);
}
// close form
this.Dispose();
}
protected virtual void OnProfileImported(ProfileImportEventArgs ea)
{
if (ProfileImported != null) // check if there are any subscribers
{
ProfileImported(this, ea);
}
}
protected virtual void OnButtonImportClicked()
{
if (ButtonImportClicked != null)
{
// fire event
ButtonImportClicked();
}
}
Form 1
public partial class frm_1 : Form
{
// child form
frm_2 frm2;
public frm_1()
{
InitializeComponent();
// do something...
// not sure if this is correct code and the correct place for it
frm2 = new frm_2();
frm2.ProfileImported += new frm_2.ProfileImportEventHandler(frm2_OnProfileImported);
//frm2.ProfileImported += frm2_OnProfileImported;
frm2.ButtonImportClicked += new frm_2.ImportButtonClickedEventHandler(frm2_ButtonImportClicked);
// In creation/init:
frm2.HaveData += DataFromForm2;
}
void frm2_OnProfileImported(object sender, ProfileImportEventArgs e)
{
// do something
}
void frm2_ButtonImportClicked()
{
// do something
}
private void DataFromForm2(IList<string> data)
{
// Process the data from Form2.
}
}
What am I still missing? Thank you for your help.
frm1.ProfileChanged += new Form1.ProfileImportEventHandler(Form1_OnProfileImported);
[…]
List<string> frmLoadProfileUSB_OnProfileImported()
First those names do not match. Second, with matching signatures you do not need (since C#2 if I recall correctly) to explicitly create the delegate. Thus:
frm1.ProfileChanged += frmLoadProfileUSB_OnProfileImported;
However, I think you have the event in the wrong place. It appears it is Form2 trying to pass data to Form1. Thus the event needs to be on Form2, with a delegate that is passed the data. Thus:
In Form2
public delegate void HaveDataDelegate(IList<string> data);
public event HaveDataDelegate HaveData;
// When there is data:
var copy = HaveData; // Use copy to avoid race conditions
if (copy != null) {
copy(data);
}
In Form1
// In creation/init:
Form2Instance.HaveData += DataFromForm2;
private void DataFromForm2(IList<string> data) {
// Process the data from Form2.
}
It's better not to use strong coupling.
So best solution here would be to store data in database or create proxy-object (class/struct).
like:
public (static) class ProfileChangesMonitor
{
...your logic here
}
If you want to use event handlers, you should follow the general pattern, defining a class that inherits EventArgs (supposing you want to involve a list in the event) in this way:
// Event Args
public class ProfileImportEventArgs : EventArgs {
private IList<string> list;
public ProfileImportEventArgs(IList<string> list) {
this.list = list;
}
public IList<string> List {
get {
return this.list;
}
}
}
// Event Handler Delegate
public delegate void ProfileImportEventHandler(object sender, ProfileImportEventArgs e);
// Form1:
public event ProfileImportEventHandler ProfileImported;
// ...
private void btnImport_Click(object sender, EventArgs e)
{
// raise an event
List<string> list = new List();
// Add something to list if needed
var ea = new ProfileImportEventArgs(list);
OnProfileImported(ea);
// Use ea.list here if necessary
}
protected virtual void OnProfileImported(ProfileImportEventArgs ea)
{
if (ProfileImported != null) { // check if there are subscribers
ProfileImported(this, ea);
}
}
// Form2:
public partial class Form2 : Form
{
Form1 frm1;
public Form1()
{
// Constructor logic
// TODO: Instantiate frm1 first.
frm1.ProfileImported += new Form1.ProfileImportEventHandler(Form1_OnProfileImported);
}
}
private void frmLoadProfileUSB_OnProfileImported(object sender, ProfileImportEventArgs e)
{
// Use and/or modify e.List if needed
}
Would you look at my code and tell me where I went wrong? in following code I am trying to send a notification to myMethod() method when Form1 gets maximized.
Thanks!
namespace WindowsDelegate1
{
public delegate void ChangedEventHandler(object sender, EventArgs e);
class myForm : Form
{
public event ChangedEventHandler Changed;
protected virtual void OnChanged(EventArgs e)
{
if (Changed != null)
Changed(this,e);
}
public override System.Drawing.Size MaximumSize
{
//get
//{
// return base.MaximumSize;
//}
set
{
base.MaximumSize = value;
OnChanged(EventArgs.Empty);
}
}
}
}
namespace WindowsDelegate1
{
class EventListener
{
private myForm TheForm;
public EventListener(myForm theform)
{
TheForm = theform;
TheForm.Changed += new ChangedEventHandler(myMethod);
}
private void myMethod(object sender, EventArgs e)
{
MessageBox.Show("hey, window should be maximized now!");
}
public void Detach()
{
TheForm.Changed -= new ChangedEventHandler(myMethod);
TheForm = null;
}
}
}
Here is the testing unit / or main()
namespace WindowsDelegate1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
myForm f = new myForm();
EventListener listener = new EventListener(f);
f.ShowDialog();
f.WindowState = FormWindowState.Maximized;
listener.Detach();
}
}
}
What's probably happening is the event is either fired after your .Detach() call, or is never fired at all. I would start by removing the listener.Detach() call. Generally, you attach to events when the form is created or when it loads and detach when it is unloading.
Other than that, your Detach method is problematic because it tries to remove a different ChangedEventHandler instance than the one added. If you're wrapping your methods in ChangedEventHandler you need to store the instance you added.
Thank you for sharing your ideas!
I fixed it by removing the property (not idea why I used that!!) and using method instead by:
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
OnChanged(EventArgs.Empty);
}
I have updated my source code above too
I can't figure out how to do this, heres sample code. Of what I wish to do.
public Class MainForm : Form
{
MyUserControl MyControl = new MyUserControl;
private void Button_Click(object sender, EventArgs e)
{
//Create MyEvent
}
}
public Class MyUserControl : UserControl
{
//listen for MyEvent from MainForm, and perform MyMethod
public void MyMethod()
{
//Do Stuff here
}
}
Step 1) Expose an event on MainForm... say..
public event Action simpleEvent
Step 2) Give MyUserControl a constructor that takes an instance of MainForm and bind an action to that event
public MyUserControl(MainForm form) {
form += () => Console.WriteLine("We're doing something!")
}
Step 3) raise the event in MainForm.Button_Click
if(simpleEvent != null) simpleEvent();
Note: You could register your own delegates and work with something other than lambda expressions. See http://msdn.microsoft.com/en-us/library/17sde2xt.aspx for a more thorough explanation
Your end result would look like...
public Class MainForm : Form
{
public event Action MyEvent;
MyUserControl MyControl = new MyUserControl(this);
private void Button_Click(object sender, EventArgs e)
{
if(simpleEvent != null) simpleEvent();
}
}
public Class MyUserControl : UserControl
{
//listen for MyEvent from MainForm, and perform MyMethod
public MyUserControl(MainForm form) {
simpleEvent += () => MyMethod();
}
public void MyMethod()
{
//Do Stuff here
}
}
This is how to delegate to an event of a private member, so the outside can listen to it.
public event EventHandlerType EventHandlerName
{
add
{
this._privateControl.EventHandlerName += value;
}
remove
{
this._privateControl.EventHandlerName -= value;
}
}
Another option would be to have an event in your form class:
public event EventHandler MyEvent;
And listen to the private member's event:
this._customControl.SomeEvent += this.SomeEventHandler;
With this:
private void SomeEventHandler(object sender, EventArgs e)
{
if (this.MyEvent != null)
{
this.MyEvent(this, e);
}
}
The usage from the outside in both cases will be the same:
var form = new Form1();
form1.MyEvent += (o, e) => { Console.WriteLine("Event called!"); };
The bottom line is the you must implement functionality inside your form to allow the outside subscribe/listen to inner events.
//listen for MyEvent from MainForm, and perform MyMethod
That's the wrong way around. Publishing an event in control is useful, the control cannot possibly guess how it is going to get used. It however most certainly should not know anything about an event that may or may not be available in the form that it gets dropped on. That has the nasty habit of blowing up when the form just doesn't (yet) have the event. The bad kind too, a crash at design time that puts up the White Screen of Darn and prevents you from fixing the problem.
A form doesn't have to guess, it knows exactly what controls it has. So where ever in the form you might want to raise the event, just call the control's MyMethod method directly. And if that's wrong for some reason, like removing the control but not the call, then you just get a compile error that's easy to fix.
I have two forms (C#). In one form, there is a method that takes data and stores them in a database after closing the form that I want to be on the other (the main form) to update the data. How to do it using OOP or simply to make the most beautiful and well.
Generally, when you want to let main form to be updated, you create a public method on that form and call it from the other form when it has the new data and can send them to main form. It shouldn't be a problem.
Note that if you want to send data to somewhere, you need a reference to that place, i.e. you need a reference to main form in the other form. Either pass this from main form to the constructor of the other form, or you can also store the reference in a static field in Program class (do it in Main method where you create the main form) etc.
The most OOP-friendly solution would probably be to have an event on whichever form "triggers" a data update, that is subscribed to and handled by another form's method. Here's a basic wire-up:
public class Form1:Form
{
public event EventHandler<MyDataObject> DataChanged;
...
public override void OnClosing(CancelEventArgs e)
{
//Put in logic to determine whether we should fire the DataChanged event
try
{
if(DataChanged != null) DataChanged(this, myFormCurrentData);
base.OnClosing(e);
}
catch(Exception ex)
{
//If any handlers failed, cancel closing the window until the errors
//are resolved.
MessageBox.Show(ex.Message, "Error While Saving", MessageBoxButtons.OK);
e.Cancel = true;
}
}
}
...
public class Form2:Form
{
//Called from wherever you would open Form1 from Form2
public void LaunchForm1()
{
var form1 = new Form1();
form1.DataChanged += HandleDataChange;
form1.Show();
}
private void HandleDataChange(object sender, MyDataObject dataObj)
{
//Do your data validation or persistence in this method; if it fails,
//throw a descriptive exception, which will prevent Form1 from closing.
}
}
You don't have to use an event; a simple delegate could be used as well and it would do pretty much the same thing, while also being able to be specified in the form's constructor (thus requiring the handler to be provided).
You can do something like this for updating the values in one form from another form...
Form 2 code
public event EventHandler<UpdatedEventArgs> updateEvent;
public class UpdatedEventArgs : EventArgs
{
public string SomeVal { get; set; } // create custom event arg for your need
}
protected virtual void OnFirstUpdateEvent(UpdatedEventArgs e)
{
if (updateEvent != null)
updateEvent(this, e);
}
private void button1_Click(object sender, EventArgs e)
{
UpdatedEventArgs eventData = new UpdatedEventArgs();
eventData.SomeVal = "test"; // set update event arguments, according to your need
OnFirstUpdateEvent(eventData);
}
public Form2()
{
InitializeComponent();
}
Form 1 code
public Form1()
{
InitializeComponent();
Form2 form2 = new Form2();
form2.updateEvent += new EventHandler<Form2.UpdatedEventArgs>(form2_updateEvent); // create event handler to update form 1 from form 2
form2.Show();
}
void form2_updateEvent(object sender, Form2.UpdatedEventArgs e)
{
if (e != null && e.SomeVal != null)
{
// Do the update on Form 1
// depend on your event arguments update the grid
//MessageBox.Show(e.SomeVal);
}
}
I am a person learning c#, and I have a program with a Parent form and a Child form. I want the child form to raise an event so that the Parent form can do something. I copied some code, but I am not smart enough to see what is wrong. I don't know how to correctly code the event in the child form. The error is DatasourceUpdated is not defined. Can anyone help me out with a suggested fix?
In the Child form I have
public partial class Form2 : Form
{
public EventHandler DataSourceUpdated;
...
private void button2_Click(object sender, EventArgs e) //Done button
{
if (this.DataSourceUpdated != null) //raise the event
{
this.DatasourceUpdated();
}
this.Close();
}
In the parent form I have this:
private void myAddRecord()
{
string myID = string.Empty;
string myMessage = "Insert";
Form2 myForm = new Form2(myID, myMessage);
Form2.DatasourceUpdated += ChildUpdated;
myForm.Show();
Right now, you're declaring an EventHandler, not an event. Change this to:
public partial class Form2 : Form
{
public event EventHandler DataSourceUpdated;
...
private void button2_Click(object sender, EventArgs e) //Done button
{
if (this.DataSourceUpdated != null) //raise the event
{
this.DataSourceUpdated(this, EventArgs.Empty);
}
this.Close();
}
Also, when you go to subscribe to your event, you need to subscribe to the event on the instance, not on the class:
Form2 myForm = new Form2(myID, myMessage);
myForm.DataSourceUpdated+= ChildUpdated;
myForm.Show();
This is because the event is declared at the instance level, not statically.
Form2.DatasourceUpdated += ...
you are trying to attach your handler to the class try this instead
myForm.DatasourceUpdated += ...
Your code looks right, as far as I can tell, as long as you have an actual handler; you have not included that in your code. ChildUpdated needs to be a method that with the signature void (object sender, EventArgs e), and you should also raise the event like that this.DataSourceUpdated(this, null);
The signature is being specified by the fact that you're declaring the event as being handled by System.EventHandler, which has that signature. You can create your own delegates as well, if you want it to receive some special parameters or no parameters at all.
Also, you have an inaccurate casing in your example, this.DatasourceUpdated -> this.DataSourceUpdated, but I'll assume that's just in your example...?
.NET events have both a "sender" object and an "EventArgs" object. These need to be included when your event is called.
for example:
private void button2_Click(object sender, EventArgs e) //Done button
{
if (this.DataSourceUpdated != null) //raise the event
{
this.DatasourceUpdated(this, EventArgs.Empty);
}
this.Close();
}
First of all there's a small typo: DatasourceUpdated vs DataSourceUpdated. See the capital S? Also, don't forget the args and to declare the DataSourceUpdated as an event:
public event EventHandler DataSourceUpdated;
...
this.DataSourceUpdated(this, EventArgs.Empty);
Another problem I notice is that your calling a static member when you should be calling an instance member:
Form2.DatasourceUpdated += ChildUpdated;
to
myForm.DatasourceUpdated += ChildUpdated;