This is going to be my first post in a series about my experience using Gtk#, a choice I made a few months ago when I decided to build my next .Net app on Linux and Mono. I've always thought that Mono was worth exploring and as I've mentioned before, the experience has been very easy going so far.
The wikipedia entry on
Gtk# says that it
is basically a set of .Net bindings for the GTK+ GUI toolkit, which stands for GIMP ToolKit. GTK+ was created specifically for the UI of the GIMP, the most popular open source raster graphics editor. Since then, GNOME, a widely used window manager that runs on top of X11, also started using this library for its UI and by extension all software written for GNOME also uses GTK+.
If you would like to follow along and try out the code examples in this series, it would be easier to install Mono on your Linux distribution of choice, or install Gtk# and Mono on your Windows machine, which you can download from
here. All of my examples have been created with
MonoDevelop and I encourage you to try it out, simply because of its UI builder. Using Visual Studio will work, but to take advantage of having all your UI Gtk# code generated for you, MonoDevelop is a must.>
Differences between WinForms and Gtk#
I assume that most of you, just like myself, come from a .Net background with a lot of experience building Win Forms or (gasp!) Web Forms. As I learn more and more about Gtk#, I discover new tricks and tools that I suddenly wish I'd have know about in the past. As WinForms developers, or even web developers in a Windows world, we are very much isolated from different technologies that exist outside of Microsoft's ecosystem. So for me, discovering and using Gtk# seemed like a bit of fresh air sprinkled with some wonderment!
So let's see what is different between WinForms and Gtk#.
The main difference is that WinForms use a static layout scheme, whereas Gtk# layouts grow and shrink automatically as you re-size the window, which has technically always been a pain to do on a WinForm application. I mean, how many of you have used SQL Server in the past and just stood there in puzzlement over why the dialog boxes were not resizable, forcing you to scroll down or to read a 100 pixel wide textbox with at least 200 characters in it?. How much time have you also spent trying to make your apps resize gracefully?
Gtk# controls, which are called widgets, must be part of a container and will either fill up the container's space or make the container shrink to fit the widget. Containers can be embedded within other containers too, which allows you to layout your UI before adding the widgets in. All of this will be demonstrated in further articles, but my point right now is that all of this is baked into Gtk#. You don't have to worry about it. And the best thing is that it works great and is really intuitive.
Simple example (click me button and text box)
Let's start by creating an empty Gtk# application which, once compiled and lauched, will result in the typical empty window:

Now let's take a peek at the code that was generated to make this happen:
1: using System;
2: using Gtk;
3:
4: namespace Gtk
5: {
6: class MainClass
7: {
8: public static void Main (string[] args)
9: {
10: Application.Init ();
11: MainWindow win = new MainWindow ();
12: win.Show ();
13: Application.Run ();
14: }
15: }
16: }
First, a quick work on the Gtk namespace. I named my project "Gtk", which doesn't seem to cause any namespace name clashes. However, it might confuse you into thinking that you have to use the same namespace for things to "just work". You don't. So please disregard it.
Pretty standard stuff, except maybe that the Application class doesn't seem to be connected to the main window. The main window needs to be shown *and* the application needs to be ran. Application.Init is first called with no arguments. You could pass in the program name and the program arguments if you'd like, but the default "empty" application doesn't seem to need to. Then, the main window is instanciated and shown on the screen. Finally, Application.Run starts the main event loop. You will see later that when closing the main window, a call is made to Application.Quit. That is how the main event loop exits and you app is freed from memory.
Now let's take a look at the MainWindow class:
1: using System;
2: using Gtk;
3:
4: public partial class MainWindow: Gtk.Window
5: {
6: public MainWindow (): base (Gtk.WindowType.Toplevel)
7: {
8: Build ();
9: }
10:
11: protected void OnDeleteEvent (object sender, DeleteEventArgs a)
12: {
13: Application.Quit ();
14: a.RetVal = true;
15: }
16: }
MainWindow is defined as a TopLevel window, which is almost always going to be the case, unless you want a popup. Specifying the TopLevel type make Gtk# connect the window to the OS or window manager. The Build method takes care of visually creating the UI. It has been defined elsewhere, thanks to the fact that MainWindow is a partial class. This just smell like code generation! We'll take a look very soon. But first, I want to mention that the OnDeleteEvent method is responsible for terminating the Application's main event loop.
Alright, let's look at the other part of MainWindow. To see it right click on the project in the solution explorer, go to display options and check "Show all files". A gtk-gui folder will appear in the solution explorer. Once expanded, it will reveal three files: MainWindow.cs, generated.cs and gui.stetic. Open up MainWindow.cs:
1: public partial class MainWindow {
2:
3: protected virtual void Build() {
4: Stetic.Gui.Initialize(this);
5: // Widget MainWindow
6: this.Name = "MainWindow";
7: this.Title = Mono.Unix.Catalog.GetString("MainWindow");
8: this.WindowPosition = ((Gtk.WindowPosition)(4));
9: if ((this.Child != null)) {
10: this.Child.ShowAll();
11: }
12: this.DefaultWidth = 400;
13: this.DefaultHeight = 300;
14: this.Show();
15: this.DeleteEvent += new Gtk.DeleteEventHandler(this.OnDeleteEvent);
16: }
17: }
Yuck, ugly code! Obviously, this code has been generated (you might have to compile the solution first.) Here is where the Build method is defined. It is a virtual method that can be overriden in subclasses, which I have found to be very useful when creating different views that can be displayed in an application I'm building. Here, the title, position and default size of the window are specified and the DeleteEvent is set to call the OnDeleteEvent method we say earlier.
What about the Stetic.Gui.Initialize call? Well, by default, MonoDevelop utilizes Stetic as the Gui designer for your applications. As you lay out your widgets and containers, the gui.stetic file that was mentioned earlier is built. It is simply an xml file (oh, noes!) that holds every possible detail about your UI. Let's take a quick look:
1: <?xml version="1.0" encoding="utf-8"?>
2: <stetic-interface>
3: <configuration>
4: <target-gtk-version>2.12</target-gtk-version>
5: </configuration>
6: <import>
7: <widget-library name="glade-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
8: <widget-library name="../bin/Debug/Gtk.exe" internal="true" />
9: </import>
10: <widget class="Gtk.Window" id="MainWindow" design-size="400 300">
11: <property name="MemberName" />
12: <property name="Title" translatable="yes">MainWindow</property>
13: <property name="WindowPosition">CenterOnParent</property>
14: <signal name="DeleteEvent" handler="OnDeleteEvent" />
15: <child>
16: <placeholder />
17: </child>
18: </widget>
19: </stetic-interface>
Our MainWindow is defined on line 12 and looks awefully like the ugly generated code from above. What happens is that as you build your UI with MonoDevelop, the stetic file is generated. When you compile your application, the ugly partial MainWindow class is automatically re-generated and then compiled.
So there it is, our first empty window application.
Next time, we'll go in a add... a button! See you then!
Posted
Sep 29 2009, 09:18 PM
by
Louis Salin