Widgets & Event Handlers

IronPython & Windows Forms, Part III

Windows Forms


This is part of a series of tutorials on using IronPython with Windows Forms.


In order for a program to do anything useful, it needs to provide a way for user input and be able to respond appropriately to that input.

For "rich client GUI" programs, the different ways of receiving input are fondly known as widgets. These include the menus, buttons, toolbars, check buttons, and text entry boxes (and so on) that we are all so used to. In .NET speak these are called controls.

In order to respond to user input, your program needs to layout these widgets within the GUI, and set functions to be called when the user performs actions like selecting a menu item or clicking on a button.

The Windows Form API makes creating widgets and registering the callback functions very easy, the layout can sometimes be a bit weird; but such is life.

The fact that all widgets are controls, means that they all inherit from the .NET base class Control. When (if) you browse the documentation for individual widgets, you will often see the phrase Inherited from Control beside properties and methods. This is very convenient. Once you know how to set the Width, Height or Location of one widget, you know how to do it for all of them.

OK, enough waffle, let's play with a widget. The most basic widget is the Label. It displays text on the GUI.

The Label

If you look in the example code on the Label class documentation page, you can see the following in C# :

Label label1 = new Label();

So we can see that the Label constructor takes no arguments.

The Label Members documentation page gives us a list of all the properties and methods (public, private and protected [1]) of the Label class.

In order to set the displayed text, location and size of a label in its parent control, we use the following properties :

If we don't specifically set any of these, then defaults will be used. The default text will look pretty dull. Laughing

The Location property needs a Point object [3] from the System.Drawing namespace to set it. As you can see in the code example, a Point is initialised with two integers.

In a minute we are going to add the following code to our example :

self.label = Label()
self.label.Text = "Please Click Me"
self.label.Location = Point(50, 50)
self.label.Height = 30
self.label.Width = 200


The last line of this snippet is important. To add a widget to a GUI you need to add it to the list of controls on it's parent widget. So uhmm... how do you do that ? Like that... Smile


The order you add controls to the parent affects the layout the GUI.

The Button

A GUI with just a label is too trivial even for me. Let's inject some interactivity and add a Button as well. To make it almost too exciting to handle, we'll make our program do something when the button is pressed.

A button being pressed is an event. So is a mouse click and a key being pressed. In order to handle an event we need to create a function with the right signature (that takes the right arguments) and register it as the handler. Fortunately, IronPython makes this ridiculously easy.

If you look at the Button Members documentation page, you can see a list of all the public events associated with buttons. Most of these are inherited from the Control class, but the one we are particularly interested in is the Click event.

The doc page on the Click event shows an example of creating a Click event handler. The C# example starts with :

private void button1_Click(object sender, System.EventArgs e)

This declares a method that takes two arguments, sender and event (called e here). The void means that it returns nothing.

The method signature helpfully tells us that sender [4] will be an object and the event will be an instance of the EventArgs class from the System namespace.

Luckily in the case of a button click we probably don't need any information from the event, we just want to respond to the action.

The import thing to know, is how to register the event handler. Here it is :

button = Button()
button.Click += self.buttonPressed

Simply add in place (using the += operator) the event handler function to the event on the control.

Hello World 2

Putting it all together, here is the full code for our second example (including the buttonPressed method) :

import sys

import clr

from System.Drawing import Point
from System.Windows.Forms import Application, Button, Form, Label

class HelloWorldForm(Form):

    def __init__(self):
        self.Text = 'Hello World'

        self.label = Label()
        self.label.Text = "Please Click Me"
        self.label.Location = Point(50, 50)
        self.label.Height = 30
        self.label.Width = 200

        self.count = 0

        button = Button()
        button.Text = "Click Me"
        button.Location = Point(50, 100)

        button.Click += self.buttonPressed


    def buttonPressed(self, sender, args):
        print 'The label *used to say* : %s' % self.label.Text
        self.count += 1
        self.label.Text = "You have clicked me %s times." % self.count

form = HelloWorldForm()

When the button is pressed the buttonPressed method is called. This prints a message to the console (getting rid of that will be the subject of another entry). This message uses the Text property of our widget.

The count is then incremented, and the label Text is changed. Cool

When you run it, it will look something like this :

Hello World 2

and this :

Hello World 2 after clicking around a bit

If you run it on Mono [5], it will look like this :

Hello World 2

and this :

Hello World 2 after clicking around a bit

In actual fact you now do know most of the basic things you need to in order to create GUI applications using Windows Forms, probably a bit more to go before it's really useful though.

In this entry we used Location to position our widgets in the main form. Still to be covered are other layout methods, changing the appearance of widgets, menus, toolbars and lots more widgets as well.

[1]Protected methods are only available from within instances of the class and its subclasses.
[2]Instead of using the Width and Height properties, we could use the Size property. If you look at the code examples, you can see it takes a Size object (from the System.Drawing namespace).
[3]Point is actually a structure rather than a class. That doesn't really matter though.
[4]The sender is actually the control the event is on.
[5]Again, Mono screenshots thanks to Seo Sanghyeon.

For buying techie books, science fiction, computer hardware or the latest gadgets: visit The Voidspace Amazon Store.

Hosted by Webfaction

Return to Top

Page rendered with rest2web the Site Builder

Last edited Fri Nov 27 18:32:35 2009.