.NET on Linux: C# Development with Mono and Glade, Part II

Now that you have a window you need to ensure that it will close properly. This involves making a single call to the Gnome API function named Application.Quit when the window is closed. If you don’t respond to the closing of the window, then the application will not exit properly. In particular, the application will remain in memory, even though the main window is closed. This might seem strange at first. However, if you think about it for one second, it makes sense. The window is being closed, but the act of closing a window is not really the same thing as exiting the application. So we need to respond to the closing of the main window in an application by also quitting or exiting the application itself. This section and the next explain how this process works.

The trick, of course, is knowing when the window is being closed. Fortunately GTK can send you something called a delete_event signal when the window is being closed. If you are Delphi programmer, you are probably used to thinking in terms of events, rather than signals. This is just a matter of terminology. A signal is the same thing as an event. A delete_event is associated only with windows; there is no delete_event sent when controls are destroyed. Instead, controls emit destroy events.

As mentioned above, in GTK terminology, an event is called a signal. Turn to the signals page in the property inspector, click on the Signal ellipses button and hunt for the delete_event. After selecting it, type in the name of its handler. This name, which you can make up, will be the name of the corresponding routine in your source code that will handle this event. In this case, the handler will be called MyWindowDeleteEvent.

After selecting the delete_event from the Signal ellipses button, I have typed in the name of the event handler, which in this case is called MyWindowDeleteEvent.

Let’s take a moment to make sure you understand what is going on here. The control we are working with, which happens to be a window called myWindow, automatically emits an event when it is closed. For instance, if the user selects the X in the upper right hand corner of this window at run time, then the delete_event will be fired. The developer then needs to trap this event and respond to it. One typically traps an event by writing a method that will be called automatically when the event is fired. In this case, we are making a contract, promising that we will create a method called MyWindowDeleteEvent, that will have the right signature, that is, it will accept the parameters that the delete event passes when it is fired. If we fulfill our half of the contract, then the method will be called automatically.

After setting up your event handler in Glade, you should press the Add button. This will add the event to the window.

The event, or signal, has been added to the window by filling out the dialog  and then pressing the Add button.

You can now choose the Save button in the main Glade window to write your project to disk. Give your project an appropriate name, such as GtkStart05. After saving the project, and assuming you use the proposed naming scheme, you will find two files on disk generated by Glade: GtkStart05.glade and GtkStart05.gladed. The first file uses XML syntax to describe the window and signals you created. In the next section of this article you will learn how to automatically parse this file and then automatically create the controls and signals you defined. This entire process can be done with two, short, simple, lines of code. The second generated file, the one that ends in gladed, is a project file. The Glade development environment uses this XML file to manage your project.

Writing Version 0.5 Of Our Program

We have now used Glade to create a main window, and have ensured that it will fire off an event when the window is closed. The next step is to write source code that will create an application, create the main window, display the window, and respond properly when the user closes the main window. In our case, the proper response to closing the main window is to close the application itself.

If we were working in C, C++ or Ada, then Glade could create the source code we need automatically. This is similar, though not identical, to the process in Delphi or Visual Studio, where the code for the application is not only created, but integrated into the same tool set as the visual designer. In particular, you can choose Project | Options from the Glade main window, and select whether you want to generate C, C++ or Ada code. After making your choice, you can press the Build button on the Glade main window to generate the actual source code. Note that the Build button does not compile or link your source, it only generates the files you need to compile your program. If you are creating a C program, you can then go to the command line, run the script called autogen.sh, and the make files for your system will be created automatically. Then type make at the shell prompt and your project will be automatically compiled. I should add that there are tools in other languages, such as Python, Perl and Ruby, that will read a file like GtkStart05.glade and then automatically generate the needed source code to create the windows described in it.

But in this case we are not working in C, C++, Python or Ada, and so we need to write code by hand in a text editor. Fortunately, that process is simple, since the required code is brief and easy to understand. It is shown in Listing 1. The code to compile and link the project is shown in Listing 2. To run the project, type mono gtkstart05.exe. If all has gone well, you should compile cleanly with no errors or warnings. When you run the project, a plain window should appear, as shown in Figure 7. When you close the main window, you should be taken back to your shell prompt. If the window disappears, but you are not taken back to the prompt, then something is wrong with your signal handler. You can, however, still press Ctrl-C or Ctrl-D to end the application and return to the shell prompt.

The simple, empty window that is created after we compile our source to create gtkstart05.exe, and then run that binary file from the command line by typing mono gtkstart05.exe.

Listing 1: The source code for version 0.5 of our simple introductory application.

namespace GtkStart
{
        using System;
        using Gtk;
        using Gnome;
        using Glade;
        using GtkSharp;
        public class GtkStart05
        {
                public static void Main (string[] args)
                {
                        new GtkStart05(args);
                }
                public GtkStart05(string[] args) 
                {
                        Application.Init();
                        /* Load the Glade xml file */
                        Glade.XML gxml = new Glade.XML ("gtkstart05.glade", "myWindow", null);
                        gxml.Autoconnect (this);
                        Application.Run();
                }
                /* Fulfill the contract we made in Glade to create a signal handler */
                public void MyWindowDeleteEvent (object o, DeleteEventArgs args) 
                {
                        Application.Quit ();
                        args.RetVal = true;
                }
        }
}

Listing 2: Code to compile the project. Note that a path is temporarily defined pointing to the default location for the gtk-sharp libraries.

export MONO_PATH=/usr/lib/mono/gtk-sharp/
mcs /unsafe -r gtk-sharp.dll -r glade-sharp.dll -r gnome-sharp.dll gtkstart05.cs
export MONO_PATH=

The code shown in Listing 1 has a Main method that creates an instance of the GtkStart05 class, just as you would in any normal C# or Java application. The constructor begins by calling the predefined Application.Init routine from the GTK API. Needless to say, the purpose of this routine is to initialize your GTK based application.

The next line of code loads in the XML file that you created in Glade. Note that we have optionally chosen to pass in the name of a specific object, namely myWindow. The Glade API uses this window name to know which part of the XML file it is going to parse. If you don’t pass in any name, then it parses the whole file.

Note that Application.Init is part of GTK#, while Glade.XML is part of the Glade API, or more particularly, it is part of libglade. It is worth taking a moment to think about libglade. It is possible to create GTK widgets by hand. In other words, you don’t have to parse an XML file in order to create a window or a button when using GTK. Instead, you can create these controls by hand by writing code. Code of this type, that is code not based on XML, is generated when you push the Build button in the Glade main window. What libglade does is provide routines that parse the XML and automatically create the controls you want to access. The great advantage of this system is that it at least partially separates your user interface code, which is defined in XML, from the rest of your source code, which is written in C#. Of course, the separation is not complete, since some of your user interface code, such as event handlers, are in your hand written source code. But this system will allow you to regenerate the interface with Glade without overwriting any of the code in your source.

The libglade API’s and its related programming philosophy are described in depth at this URL:

http://developer.gnome.org/doc/API/libglade/libglade-notes.html

After creating an instance of type Glade.XML, the next step is to call Autoconnect.

 Glade.XML gxml = new Glade.XML ("gtkstart05.glade", "myWindow", null);
 gxml.Autoconnect (this);

The call to Autoconnect hooks up the signals defined in the XML file to the event handler you have defined in your file. In other words, it focuses on this bit of XML from gtkstart05.glade:

<signal name="delete_event" 
 handler="MyWindowDeleteEvent" 
 last_modification_time="Thu, 18 Nov 2004 17:42:01 GMT"/>

It then hooks that XML encoded signal up to this signal handler from Listing 1:

public void MyWindowDeleteEvent (object o, DeleteEventArgs args) 
{
  Application.Quit ();
  args.RetVal = true;
}

In this code, note the call to Application.Quit. Needless to say, this call closes the application. It ends the application process and takes the user back to the shell prompt, or the GUI desktop. The signal handler is written in order to provide a place to make this call. In other words, the program responds to the delete_event so that it can make this call, and thereby smoothly end the program.

For the purposes of review, let’s take a second look at the constructor:

public GtkStart05(string[] args) 
{
  Application.Init();
  Glade.XML gxml = new Glade.XML ("gtkstart05.glade", "myWindow", null);
  gxml.Autoconnect (this);
  Application.Run();
}

By this time we have examined all but the last call. As you can see, the sequence of calls in the constructor is completed by calling Application.Run, which is part of the GTK# toolkit. This call hands the control of the program over to the GTK. Your program never fully gets control back again until you call Application.Quit. Instead, your code simply processes signals of various types, such as those that are generated when buttons are pushed or data is entered in various types of controls. Your program continues to respond to events until such time as you call Application.Quit. At that point, your program exits.

Summary

That is enough for this article. In the next installment we will begin populating our window with controls, and responding to events generated by those controls.

In this article you learned how to create a simple window using the Glade development environment. You also saw how to create a simple signal, or event, that would fire when the window was closed. After creating the window and the signal, the article described how to save the information about your window to an XML file. The article explained how to load and parse the XML file, and how to connect the signal defined in the XML file to an event handler defined in your code. Finally, you saw how to respond to an event by closing the application itself.