/// Thursday, November 10
Use XSLT Transformations to Ease Glade#
Summary
This article describes how to use the XslCodeBuilder to simplify cross-platform Gtk# application development.
Introduction
In a previous article, I described the implementation of XslCodeBuilder to inject code generated by an XSLT transformation. In this article we'll apply XslCodeBuilder to ease Gtk# development.
There are at least two ways to build a Gtk# application. The first is to use the Gtk# library as is, manually creating widgets and compose them in a hierarchy to build your GUI. The easier way is to use the Glade# library. Draw your GUI using the Glade 2 utility then save the project creating XML metadata in a .glade file. The .glade file is loaded by the glade-sharp library to render your GUI.
Glade development is summed up in these steps:
- Draw your GUI - use the glade utility to generate a .glade file
- Declare variables for widgets - each widget in a .glade file has a unique id
- Set event handlers - each widget has a set of 'signals' or events
- Load the .glade file and run
The rest of the article describes the use of XslCodeBuilder to semi-automate these steps.
XslCodeBuilder and the Glade stylesheet
A .glade file is simply an XML file. It's simple enough to create a stylesheet to generate Boo code to perform the steps described above:
01 <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
02 <xsl:output method="text" />
03 <xsl:template match="/">
04 <xsl:apply-templates select="glade-interface/widget[1]"/>
05 def RunGlade():
06 RunGlade(null)
07 end
08
09 def RunGlade(init as callable):
10 Application.Init()
11 gxml = Glade.XML("view.glade", "window1", null)
12 gxml.Autoconnect(self)
13
14 if init != null:
15 init()
16 end
17
18 Application.Run()
19 end
20 </xsl:template>
21
22 <!-- build variables -->
23 <xsl:template match="widget">
24 [Widget]
25 <xsl:value-of select="@id"/> as <xsl:value-of select="substring(@class, 4)"/>
26 <xsl:apply-templates select="child/widget" />
27 </xsl:template>
28 </xsl:stylesheet>
Line 03 is the entry point into the stylesheet. The apply-templates statement starts a recursive descent into the XML file invoking any template that matches the current XML node. In this example, there is a template defined for match="widget". The template emits a declaration like this:
[Widget] id as widgetClass
After all the widgets are found, processing continues at line 05 which emits overloaded methods of RunGlade(). The XSL-savvy will realize this stylesheet only acts on the first widget from the root and all its children. This is just a sample stylesheet. In all honesty I'm a Glade# NoOB. I need a better understanding of Glade to create a robust stylesheet.
Anyway, here is the source of a basic Glade# application using XslCodeBuilder and the stylesheet:
import Gtk from "gtk-sharp"
import Glade from "glade-sharp"
import MGutz.CodeInjection from "MGutz.CodeInjection"
[InjectCode(XslCodeBuilder, "view.glade;glade.xsl")]
public class GladeView:
def Run():
RunGlade()
InjectCode generates this code and stores it in a file so you can step through it in the debugger if needed:
[Widget]
window1 as Window
[Widget]
vbox1 as VBox
[Widget]
toolbar1 as Toolbar
[Widget]
openButton as ToolButton
[Widget]
quitButton as ToolButton
[Widget]
scrolledwindow1 as ScrolledWindow
[Widget]
textview1 as TextView
def RunGlade():
RunGlade(null)
end
def RunGlade(init as callable):
Application.Init()
gxml = Glade.XML("view.glade", "window1", null)
gxml.Autoconnect(self)
if init != null:
init()
end
Application.Run()
end
Create and compile a simple controller then run it:
o = GladeView() o.Run()

If you click on either button nothing will happen. We need to hook events and display a FileChooserDialog when the Open button is clicked.
[InjectCode(XslCodeBuilder, "view.glade;glade.xsl")]
public class GladeView:
def Run():
RunGlade(Init)
def Init():
quitButton.Clicked += OnQuitButtonClicked
openButton.Clicked += OnOpenButtonClicked
def OnQuitButtonClicked():
Application.Quit()
def OnOpenButtonClicked():
fcd = FileChooserDialog(Title: "Select File")
fcd.AddButton("OK", ResponseType.Accept)
fcd.AddButton("Cancel", ResponseType.Cancel)
try:
response = cast(ResponseType, fcd.Run())
if (response == ResponseType.Accept):
file = fcd.Filename
using reader = StreamReader(file):
textview1.Buffer.InsertAtCursor(reader.ReadToEnd())
ensure:
fcd.Destroy()
The overloaded RunGlade(callable) is used to set event handlers. The handlers are self-explanatory. Run the controller, then click open

File loaded

Conclusion
I've shown how to use XslCodeBuilder to transform Glade's XML metadata to inject Boo code into a class.
Glade masters are probably thinking this doesn't save much time. I agree somewhat. The point is it does save time. When partial classes are merged into Boo event handlers can be autogenerated into a partial file to survive round-trips between regeneration. I didn't attempt to do that in this example as it's bad kharma to modify the source of the decorated class.
Comments:
Do you realize that you're quite near to implement a compiled XML general class instantiator รก la MyXaml?
Please, compare your work with this article.
<< Home
