How to handle form events in C#

P

Peter Duniho

Hello,

I am absolute beginner in C# so pardon me if the following question is
too easy.

I have a form with a one button. When I click the button I want to
display a message that reads "Button was clicked"
However, I do not want to handle the click event from that form,
instead I want to "transfer" that event to a "controller" class that
shows that message.

That's fine. By default, when you double-click on a button, the VS
Designer creates a new method in the same form and adds some code to the
InitializeComponent() method that subscribes that method to the event.

But the delegate method that gets subscribed to the event can be any
delegate method from any class (as long as the method has the correct
signature, of course...that is, it has a "void" return type, and two
parameters, the first an "object" and the second an "EventArgs").

In general, you need a method that looks like this:

void ClickHandler(object sender, EventArgs e) { ... }

And you need a reference to the button:

Button button = ...;

To subscribe, you just do this:

button.Click += ClickHandler;

Of course, the actual names can be anything you want. Also, since the
Click event is in the Control class, your variable need not actually be
of type Button; it can be Control if you like...you just have to make
sure you get the right control instance somehow.

If you set a Click handler the usual way in the Designer, and then go
look at the InitializeComponent() method, you'll see it does essentially
the same thing as the above.

On the assumption that your controller class is the one instantiating
form in the first place, it would be easy enough to subscribe to the
event at that time, from the controller class itself. The event is just
a member on the button, called "Click".

The one thing that's problematic is that, by default, control instance
variables in the form class are private. So the controller class would
not normally have direct access to the button's reference. However,
there are a number of ways around this:

* Make the button instance variable public. This is simply a
matter of setting the appropriate property in the Designer ("Modifiers"
under the "Design" category of properties). Then the controller can
access it through the form class, using the actual name of the variable.

* Enumerate the child controls. You can use Find() or similar on
the Form.Controls collection and look for the button control with the
name you want to watch. Once you have a reference to the instance, it's
a simple matter to subscribe to the event.

* Define some sort of API in the form class through which you can
pass a delegate instance to be subscribed to the button.

* Make the controller's handler method visible to the form class,
and let the form subscribe the handler to the event.

Finally, yes...you could do as Alberto suggests and define a whole new
event on the form class to which the controller can subscribe, and then
have the form mediate between that event and the button itself. But
IMHO, that's a waste. Your controller class can easily subscribe
directly to the button's event, and creating an event is -- while not
that difficult -- certainly an added complication that is in this case
wholly unnecessary.

Note that this is not a complete list. They are just some of the more
straightforward ways to deal with the issue. Which one is more
appropriate may well depend on other aspects of your design (for
example, you may not feel it's appropriate for the form class itself to
do things that seem like "controlling the controller", such as the
fourth item above).

Hopefully this gets you pointed in the right direction.

Pete
 
M

menashay

Hello,

I am absolute beginner in C# so pardon me if the following question is
too easy.

I have a form with a one button. When I click the button I want to
display a message that reads "Button was clicked"
However, I do not want to handle the click event from that form,
instead I want to "transfer" that event to a "controller" class that
shows that message.

The ides is this:
Have a class (call it "controller")
Controller opens the form
When I click the button the controller is "notified" by the form that
the button was clicked
The controller displays the message "Button was clicked"

Now, how do I accomplish this?

Thank you, Richard
 
A

Alberto Poblacion

I have a form with a one button. When I click the button I want to
display a message that reads "Button was clicked"
However, I do not want to handle the click event from that form,
instead I want to "transfer" that event to a "controller" class that
shows that message.

The ides is this:
Have a class (call it "controller")
Controller opens the form
When I click the button the controller is "notified" by the form that
the button was clicked
The controller displays the message "Button was clicked"

Now, how do I accomplish this?

One way to accomplish this is to publish an event in your form. When the
controller opens the form, it subscribes to the event of the form. In the
click event of the button, the only action that you take is to fire the
event that you published in your form. In this way, it gets "relayed" to the
controller.
 
R

Richard_AU

Alberto,
Thank you for your reply
As I have mentioned I am a beginner with C# so I
need some code exmples.
"publish an event", "subscribe to the event" etc
what is the code that you use in your form and what
is the code that you use in your controller class?
 
G

Guest

public class From
{
public event EventHandler BtnClicked;

private void Clicked(object o , eventrags e)
{
if(BtnClicked != null)
BtnClicked (o,e) ;
}
}

public class Controller
{

private void OpenFrom()
{
From frm = new From ();
frm.BtnClicked += new EventHandler(FromClickedHander);
Frm.shopw();
}

private void FromClickedHander(object o , eventrags e)
{
.....
}

}
 
A

Alberto Poblacion

Richard_AU said:
As I have mentioned I am a beginner with C# so I
need some code exmples.
"publish an event", "subscribe to the event" etc
what is the code that you use in your form and what
is the code that you use in your controller class?

To publish an event inside your form, you declare it inside your class
like this:

public event EventHandler TheButtonClicked;

You then "fire" it from the click of the button:

protected void Button1_Click(object sender, EventArgs e)
{
if (TheButtonClicked!=null) TheButtonClicked(this, e);
}


The "controller" subscribes a routine to handle the event connecting the
routine to the event after creating the form:

TheForm f = new TheForm();
f.TheButtonClicked += new EventHandler(MyRoutine);
f.Show();
....

private void MyRoutine(object sender, EventArgs e)
{
//Do something here
}
 
R

Richard_AU

Thank you everyone for your assistance I now understand how to use this
events mechanism.

I got it to work but there is a little problem.
I can make it work if I create another form and from that form I load my
"controller" class.
How can I load my "controller" class when the application starts?
That is,when I start my application on the C# IDE it will load my controller
class, as a result my controller class opens my form.
Then if I click a button on that form it will instruct my controller class
to unload (resulting in closing my form)?

Thanks for you help
Richard
 
P

Peter Duniho

Richard_AU said:
Thank you everyone for your assistance I now understand how to use this
events mechanism.

I got it to work but there is a little problem.
I can make it work if I create another form and from that form I load my
"controller" class.
How can I load my "controller" class when the application starts?

You mean, how do you get your controller class to actually be able to
act as the controller class, instead of the controlled class? :)

This is simple to do, once you understand the structure of the default
C# program when created within Visual Studio. The entry point for your
program is the Main() method found in the Program class, which is in the
Program.cs file. Check your Solution Explorer pane to find this easily.

In the Main() method, you'll want to change the code that by default
looks something like this:

Application.Run(new Form1());

Instead, you'll probably want to do something like instantiate the
controller class, let it instantiate your initial form, let the
controller class subscribe to whatever events are appropriate at the
time, and _then_ call Application.Run() with the form instance that the
controller created. You could even have the controller class itself
make the call to Application.Run(), which could give it a little more
control over application shutdown. Or you could have a method in the
controller class that returns the initial form.

Or, you could even have a static method in the controller that does all
of the instantiation of the controller and the initial form and then
returns that initial form.

If you did the latter, then the only change to the Program.Main() method
might look something like this:

Application.Run(Controller.FormStartup());

where the Controller.FormStartup() method creates the actual controller
instance, creates the initial Form instance, subscribes the controller
instance methods to the initial Form's event as necessary, and then
returns the reference to the Form instance.

Now, all that said...

For yet another perspective on how this might work: I don't think it'd
be the end of the world if you just wanted the initial form's
constructor to instantiate the controller. Yes, it would mean that the
controller itself isn't really doing the instantiation of the initial
form, which seems a little backwards. It also means that the form class
winds up perhaps knowing more about the controller than would normally
be the case. But as long as the controller semantics are otherwise
adhered to, I don't think that devalues the use of a controller class.

In fact, if you make the controller a singleton class with the usual
static Instance property, you can either instantiate the controller
lazily (on the first call to the Instance property getter), or in a
static constructor for the class. Either way would allow you to not
only leave the default Program.Main() method as-is, but would also avoid
having the controller's instantiation explicitly tied to code within
your initial form; _any_ form that ever accesses the controller class
could be used and the controller class would get created implicitly in
the process.

So, for example, you might design it so that rather than the controller
class creating that initial form, the form "registers" itself with the
controller in its constructor, allowing the controller to at that time
do whatever event subscription, etc. it needs to do. You could in fact
make that the design for all forms; controlled instances always need to
register themselves, which is the point at which the controller then
hooks up whatever event handling, etc. it wants.

IMHO, "design patterns" such as singleton, controller, etc. are for your
benefit. Adhering closely to them is often the best way to get the most
value out of them, but if there seems to be a benefit in deviating a
little from the pattern, and you can do so knowing exactly what
implications that has with respect to the overall design, I don't think
it's terrible to do so. Better to deviate a little, than to corrupt
some other part of the design just to get the design pattern to be exact
(not that I think that changing Program.Main() is a corruption...in this
case it's just a matter of what seems easier or cleaner to you).

Pete
 
R

Richard_AU

Peter,

Thank you for this exmplanation
Any chance of a small code example?


Richard
 
P

Peter Duniho

Richard_AU said:
Thank you for this exmplanation
Any chance of a small code example?

Of what? One downside of my rambling is that I offered a number of
design possibilities. You haven't narrowed the question down much from
my broad offering. :)

That said, I would probably go with the "static method in controller
class returning the form" design, if it were me. That might look
something like this:

class Program
{
static void Main()
{
// ... VS-supplied initialization
Application.Run(Controller.FormStartup());
}
}

class Form1 : Form
{
// this would be your usual Form-derived class.

// I've assumed that you've simply made the button
// instance public for the purpose of subscribing
// to its events.
}

class Controller
{
public static Form FormStartup()
{
Controller controller;
Form1 form1;

controller = new Controller();
form1 = new Form1();
form1.button1.Click += controller.ClickHandler;
return form1;
}

private void ClickHandler(object sender, EventArgs e)
{
// do whatever in response to click event here
}
}

Hope that helps,
Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top