C# delegates, events and WinForms

  • Thread starter Thread starter STom
  • Start date Start date
S

STom

I have a C# Winforms app that has 5 Winforms, lets say A through E.

A: Data entry. When data is entered here in any field, values are updated on
forms C, D, E.(Not B)
B: Data entry form. When data is entered here in any field, values are
updated on forms C, D, E (not A).

I am considering using delegates to fire events from forms A & B. In forms
C, D, E I will have functions with the same signature and even the same name
that just updates the data on those readonly screens.

Being new to delegates, I am a little confused on how to do this.

Do I:
1. Create a delegate in Form A and one in Form B.
2. Create a method in Form C, D, E where I can subscribe to the delegate
from forms A & B. This could be called on form load.

This is where my confusion starts (or possibly before)...when I subscribe to
the event in forms A and B, won't I need an instance of those form objects?
I don't think having form C, D, E have instances of Form A and B are the
appropriate way of doing it.

Am I way offbase with my thinking?

STom
 
How I'd do it personally is have an underlying data object that raises
events when its data is changed. This way is a lot more flexable.

Greg
 
Greg,

Thanks for the answer.

I will not be sending the data to the data component until I am ready to do
a save, that way they can tinker around with the numbers all they want too,
but if they don't save, they haven't screwed up their original work.
Therefore, the data component won't come into play during the time in which
I am changing the data on the screen.

STom
 
Hi Stom,

My guess is that Greg means a class of your own design that looks
after the communications regarding data changes.

But let's look at your solution - your thinking isn't as confused
as you feel.

|| I am considering using delegates to fire events from forms
|| A & B. In forms C, D, E I will have functions with the
|| same signature and even the same name that just updates
|| the data on those readonly screens.

Yes. Forms A and B will be firing events via Delegates. C, D and E
will have functions that conform. The same name isn't necessary but
makes good sense.

|| Create a Delegate in Form A and one in Form B.

Yes again.

|| Create a method in Form C, D, E where I can
|| subscribe to the Delegates from forms A & B.
|| This could be called on form load.

Yes again. And again.

|| This is where my confusion starts (or possibly before)

Well, not really and (no).

|| when I subscribe to the event in forms A and B,
|| won't I need an instance of those form objects?

Yes. Forms C, D and E will need to know who to subscribe to. So
they will need to know about A and B.

|| I don't think having form C, D, E have instances
|| of Form A and B are the appropriate way of doing it.

It's inevitable. Someone's got to know about someone to get a
connection going.

This is where Greg's idea comes back in as an alternative. By
having each Form know about his central object*, they needn't know
about each other. The object would have a port (function) through
which it receives value update information from A and B. It would have
the Delegate that C, D and E hook themselves on to. So when A or B
pass in a value, the Delegate is activated, calling C, D and E.

|| Am I way offbase with my thinking?

Oh yes. I haven't agreed with a word you've said, lol.

Regards,
Fergus

* Because there's only need for one, you could use a static class.
This makes it effectively global - so no need for passing it around.
 
Fergus,

Thanks a lot, that makes more sense now.

I have been tinkering around with how to do this using a winform app that
has two forms. I am having some trouble with getting the event to not be
null when the event is fired. Here is the code from this new apps

//FormA class
public delegate void SomethingChangedDelegate();
public event SomethingChangedDelegate OnMyChange;
......

[STAThread]
static void Main()
{
Application.Run(new FormA());

}

private void FormA_Load(object sender, System.EventArgs e)
{
myB = new FormB();
myB.Show();
}

private void txtNumber_TextChanged(object sender, System.EventArgs e)
{
if (OnMyChange != null)
OnMyChange();
}


//FormB class
private void FormB_Load(object sender, System.EventArgs e)
{
FormA myA = new FormA();
Subscribe(myA);
}

public void Subscribe(FormA form)
{
form.OnMyChange += new FormA.SomethingChangedDelegate(CallMeWhenChanged);
}

static void CallMeWhenChanged()
{
MessageBox.Show("I just got clicked");
}

----------------------------------------------------------------------------
---------------

Basically, when text is changed in the edit box of FormA, it checks to see
if OnMyChange is not null, but it is null so it doesn't do anything. The
subscribe seems to run fine, but I'm not sure if the reason OnMyChange is
always null is because my subscribe method is using a different instance of
FormA or not.

Thanks.

STom
 
Hi Stom,

Sorry I've taken so long to get back to you.

You were right about your new instance of FormA. Your FormA_Load is ok but
it needs to tell the second form about itself (ie pass a refernce).

private void FormA_Load(object sender, System.EventArgs e)
{
myB = new FormB (this); // Create a new FormB which knows about
this FormA.
myB.Show();
}


There needs to be a new FormB constructor which will accept another Form
for it to subscribe to.

public FormB (Form oForm) : this()
{
Subscribe (oForm);
}


Note the ": this()" which says to call the original constructor first.
This ensures that any Component initialisation is done. (Put some Controls on
the Form. Run it. Then take out ": this()" and see the difference.)

The subscription is no longer done in Load because the reference to FormA
is not available to Load().

public void Subscribe(Form oForm)
{
oForm.OnMyChange += new
FormA.SomethingChangedDelegate(CallMeWhenChanged);
}


Note that the parameter are Form rather than FormA. This generalises it so
that FormB could be hooked up to any Form which has an event conforming to the
SomethingChangedDelegate.

Note, too. The SomethingChangedDelegate needn't be declared <inside>
FormA. It can stand in its own right. This will allow <any> Form to ustilise
it. This will give:

public void Subscribe(Form oForm)
{
oForm.OnMyChange += new
SomethingChangedDelegate(CallMeWhenChanged);
}

Come back if you need more, or if I've made a mistake! :-)

Regards,
Fergus
 
Fergus Cooney said:
Note the ": this()" which says to call the original constructor first.
This ensures that any Component initialisation is done. (Put some Controls on
the Form. Run it. Then take out ": this()" and see the difference.)

You won't see any difference. The parameterless base constructor is
called automatically if you don't specify any other constructors to
call. Here's an example to show that:

using System;

public class Base
{
public Base()
{
Console.WriteLine ("Base constructor");
}
}

public class Derived : Base
{
public Derived (string x)
{
Console.WriteLine ("x={0}", x);
}

static void Main()
{
Derived d = new Derived("hello");
}
}

Note also that the parameter type would have to be FormA (or whatever)
in order to have the OnMyChange event, unless it's a standard event
that you actually want to be triggered.
 
Jon Skeet said:
You won't see any difference. The parameterless base constructor is
called automatically if you don't specify any other constructors to
call.

Doh - ignore me, I'm being silly. Yes, a call to this() is necessary -
it's a call to base() which wouldn't be necessary :)

(Fergus, I hope you get to see this one before replying to my previous
one. Sorry!)
 
Back
Top