Programming Life in the WPF – Part 3

XAML and the User Interface

What good is a simulation without an interface? Not much. Of course, the word interface can be interpreted in various ways. I have worked with simulation models in the past that chugged along behind the scenes for hours or days, a blinking cursor in a command window the only sign that anything was happening, and then ended by spitting out a bunch of data. That’s an interface, but for AvalonLife I wanted something much more interactive. The pleasure in running Life models is in watching them evolve, so the program needed a display that could render the model to screen after each iteration. There were also a number of controls implied by the design and the needs of the simulation: timing interval settings, grid size, grid type, etc. It was actually a rather simple set of requirements for an interface, and as the visual end of a project is nearly always the most satisfying for me, I didn’t have any reluctance about diving in and starting to design what AvalonLife would look like. I had this little problem, though: as a server-side developer, even as one with a long history of Windows GUI development going back to HWNDs, I had always found it hard to keep pace with the changes in Windows interface technology. I had obtained a working competency at Windows Forms development, but now as I approached the AvalonLife project I had to face the fact that Microsoft had gone and changed it all again.

What Microsoft has done with Windows Presentation Foundation is to alter the rules of the Windows client-side development game in ways that are as fundamental as, say, the change from CGI to server-side scripting in web development. In fact there are lots of parallels between the evolution of web application development and what Redmond has done with WPF. In the beginning user interfaces were created programatically: by coding instructions that told the operating system to draw lines and rectangles and words, and coding further instructions that captured user input events associated with those elements. In the case of early web development you coded instructions that generated HTML, so that a client browser could tell its operating system where to draw lines and rectangles and words. If you were a web developer HTML markup was a lot easier to get up and running than a hand-coded Windows interface written at the Win32, MFC, or ATL level. That didn’t help the guy who had to program the browser much, but it sure made it easier to get information onto the screen. In its own way, though, early web development on the server-side was just as hard. If you wanted to present anything more complicated than a static HTML page you needed to generate the markup on the server in response to some input. If you had a working page, and later decided that you wanted to change something, you had to dive into the server code, find the place where that HTML element was written to an output stream, and change the code to write something else in its place.

So the advent of server-side scripting was a huge win for web developers. In early versions we could mix HTML markup and script in server-side pages and have the server evaluate them at runtime, sending properly formatted output off to the client. It became much easier to create the static pieces of an HTML interface and mix in the dynamic parts. On the client side much of the same power was gained through javascript and the Document Object Model (DOM). But still, the mixing of HTML and script was messy. What we really needed was to be able to describe the HTML markup and styles to apply to it, and then manipulate that markup in separate code modules. To get all the way there we had to wait for Microsoft to evolve their way onto the .NET platform. The .NET framework has come to mean a lot of things, but for a programmer what it really represents is the Common Language Runtime (CLR) and Microsoft Intermediate Language (MSIL). Prior to .NET every language used whatever internal representation made sense at the time. C and it’s derivatives null-terminated strings; BASIC and its progeny did not. Pascal pushed function call parameters onto the stack in one order, while C++ pushed them in another. Each language had it’s own idea of what a module was, and how one should be loaded and linked. Each had its own idea of memory management, and what the responsibilities of a function caller were. What Microsoft did with the CLR and MSIL was define a common internal representation and runtime framework that any grammatically consistent programming language can be translated to and implemented on. Once you have that, you can take HTML, script, and binary components and translate them all to MSIL where they can cooperate on the CLR, referring to each other and making calls into each other as if they were all written using the same language, which in fact they ultimately are.

.NET, strangely named as it might be, is a logical next step on the long road from unstructured assembly through structured programming, procedural decomposition, high level languages, and object orientation. Because of the basic mechanisms in .NET we could have an HTML page derive from a C# class, and manipulate the HTML document objects from the C# code. Certainly there were issues with this model. Not least of them was the need to redeclare controls as members of the base class in your code-behind. In ASP.NET 2.0 this was refined to use two partial classes in the code behind, one you write and one the compiler generates for you, to obviate the need for manually maintaining control declarations and event wireups. With the 2.0 model ASP.NET does about as well as possible in making the markup, the script, and the server-side objects that drive the UI seamlessly interoperable. The point of this amble down Memory Lane? Aside from the fact that I think the history is important, the point is that while things were getting better for web developers, over in Windows client land UIs were still created in code. There was Windows Forms, a common and very object-oriented GUI framework that all .NET languages could use, but regardless of how seamless and feature-rich that framework was, it was still code. In practical terms that meant that it was nearly impossible to do the one thing that every web project manager takes for granted: have designers create the user interface and give it to programmers to implement. It was only a matter of time before the changes taking place in the web world made their way onto the client desktop. In the Windows world, that migration has taken place with .NET 3.0 and the WPF.

The WPF introduces Extensible Application Markup Language, or XAML, to the Windows client development landscape. XAML is a declarative syntax for describing Windows GUI interfaces. If you’re familiar with working in HTML and XML, then XAML will not look all that strange to you. Here is an example, the main window declaration from AvalonLife, which is contained in the file ALMainWin.xaml (available here as listing 3):

The XAML code above declares an instance of a Window object, and serves as the root of a tree of XAML object declarations that will make up the main interface of the program. The Window declaration, and all object declarations in XAML, follows the basic form:

Within the general form of XAML declarations there is a lot of flexibility, and I don’t intend to try and present a primer on the structure and grammar of the language here, not least of all because I am just learning it myself. There are a lot of excellent sources on the web. I do want to illustrate the general structure of the AvalonLife GUI, and along the way I may detour here and there to talk about some things I found interesting. For the moment I’ll continue building on the Window declaration above. If you look at the first line you’ll note that the attribute x:Class is set to the name of a type in the AvalonLife namespace. The x:Class attribute is a special property that can be set on the root object of a XAML file. It specifies the class that completes the partial type that the XAML file begins. Every XAML file begins with either a Window or NavigationWindow declaration. For browser-deployed apps the Page declaration is also valid at the root. Every XAML file either fully or partially describes a single class. The class is fully described by the XAML if it has no x:Class attribute on its root object. In this case there is no code-beside, and the XAML cannot declare event handling attributes, or elements of custom object types that are defined in code. In the usual situation the XAML partially describes a class that is completed by, in my case, the AvalonLife.ALMainWin class. When you use the IDE to drop a new window into a WPF project it will ask you for this name, and then generate skeleton code for the XAML declaration above, as well as the code-beside module. Assuming you’re using C#, as I am, the code-beside looks something like this:

(The full code-beside is available in listing 4) Note that this class is derived from System.Windows.Window. Every XAML Window that specifies an x:Class must set it to a type that derives from the System.Windows.Window class (or System.Windows.Controls.Page). Note also the keyword ‘partial.’ This tells the C# compiler that this class declaration is only a partial definition of the type ALMainWin, and that the rest of the class will be defined elsewhere. In this case that is in the XAML introduced above. This is identical to the syntactic mechanism used for code-beside in ASP.Net, which will make it instantly familiar to a lot of developers. As in ASP.Net, setting attributes in markup is equivalent to setting properties in the code-beside. For example I could add the following to the C# class definition:

Setting the Window.Title property to “AvalonLife 1.0″ in the C# code-beside is the same as setting the Title attribute in XAML, but keep in mind that these assignments don’t happen at the same time, and property sets in the code-beside will always override XAML declarations. Consistent with other XML-derived markups attributes can also be turned into elements by prefacing the attribute name with the entity name, as shown here:

In the example above I have set the Window.Title property as an element rather than an attribute. The usual reason for doing this is to set properties that are collections, where it isn’t convenient to specify the values of the set as attributes. As it happens there are several examples of this in the next chunk of XAML we’re going to look at, which is a severely elided listing of the entire AvalonLife main window:

I’ve removed most of the attributes and many of the child elements, in order to show the overall structure of the Window as clearly as possible. I’ve used ellipsis to indicate where additional declarations occur. As I noted above you can see several cases where elements are being used to populate collections on parent objects. It’s most readily apparent in the MenuItem children of the Menu, and the StatusBarItem children of the Statusbar. However embedded within the structure of the file are parent child relationships going all the way up to the Window object at the root. There are really two ways to visualize a well-formed set of XAML declarations: geographically and hierarchically. The geographic view sees the declarations as creating a particular effect on the screen realestate. AvalonLife, for example, follows a pretty common Windows application layout. Some might even call it outdated in the face of the changes coming in Vista.

To understand why the markup above results in the object relationships and screen layout seen at runtime, it helps to understand how the WPF layout engine works. To shed some light on it I will briefly describe each of the types of objects in the illustration and why their behavior results in the layout you see. We already looked at the Window declaration. A Window is a specialized type of ContentControl, that is, a kind of control that can contain a single piece of content (any UIElement). Window is special because it can only be the root element of a hierarchy, and so cannot be the child of any other element. A Window has a non-client area that contains the system menus, minimize/maximize buttons, and title, and a client area that contains the Window’s content. In this case that content is a Grid, which was inserted by the Visual Studio IDE when I created this window. A Grid is a control that can contain a number of rows and columns that partition it into cells that can contain content. This particular Grid has only the default single row and column, and one cell that contains all the rest of the controls. I don’t know why the IDE wants to start with a Grid, but it is a convenient way to begin, because a Grid resizes its content when it’s own size changes.

The next object is a DockPanel, which is the sole child of the outermost Grid, and inhabits its only cell. DockPanel is a very useful type of container, that is, a control that contains multiple child controls and exerts some influence on their layout and positioning. Other containers are available. Grid is one, as is StackPanel, WrapPanel, and others. StatusBar and Menu are also containers, but more specialized. What makes DockPanel very useful is it’s ability to arrange its child controls by docking them to its edges. If you look back up at the XAML window skeleton above you’ll see a number of declarations that set the DockPanel.Dock attribute. This is a strange bird. If you look up the class definitions of any of these controls, you’re not going to find an attribute called Dock, and anyway, even if there were one it wouldn’t be prefaced by the typename DockPanel. The DockPanel.Dock property is something called an “Attached Property.” I’m not going into detail about them here, and to be honest I am not yet sure whether they are an elegant solution to an interesting problem, or an inglorious hack, but in any case I can describe what they mean to you in practical terms.

When a control is declared in a certain context, as the child of a DockPanel in this example, it “inherits” attributes which let it tell the parent how it expects to be treated. In this case the DockPanel.Dock property informs the containing DockPanel where the control wants to be docked. Docking is first come, first served, meaning that the order in which the children are declared is the order in which they will be given their docking positions. The order of XAML declarations is nearly always important, and this just one example where that’s true. Others include the Menu and StatusBar, which are coming up below. If you look back at the XAML above you’ll begin to see why I have declared the controls in the order I have.

The outermost DockPanel has seven children. The first is another DockPanel, docked at the top, whose job is to arrange the Menu and some controls that live alongside it. The second child is one of four Canvases that I use to create a border effect around the center grid where the model runs. It is also docked to the top, but because it is the second child to request that position it goes under the DockPanel containing the menu. The third and fourth children of the DockPanel are a StatusBar and another Canvas. Both of these set DockPanel.Dock to Bottom, but since the StatusBar sets it first it goes all the way to the bottom of the window and the Canvas docks on top of it. When I first started laying out the XAML I had to get over an inclination to think that if the StatusBar is the last control on the window it must come last in the order of declaration. But what is important here is the order in which controls are added to the outermost DockPanel’s child list. The fifth and sixth children are the remaining two Canvases, which request Left and Right docking.

The last child of the DockPanel is another Grid that does not request any docking position at all. There are some special considerations in how a DockPanel treats the last of its child controls. DockPanel has a property called LastChildFill which defaults to true, and results in the last child of the DockPanel being stretched to fill the remaining space. How much space remains depends on the docking of the previous children, and how their widths and heights and content arrangement properties are set. I haven’t shown any of those details in the XAML skeleton above, but you can review the listing in ALMainWin.xaml to see what the values are. Note that there are some specific practices regarding creating a “flowable” layout in WPF that I have not done a good job of adhering to, given the nature of this application, so take it for what it’s worth. If you set LastChildFill to false then you must specify a docking position on that child. For my purposes the default behavior is correct to keep the center Grid stretched between the canvases that form the border.

Once you have all this set up it’s pretty cool to compile and run the app, and see all the elements line up and lay themselves out as specified, regardless of Window size. There’s so much going on under the hood to make all this possible that you would think the whole thing would be dead slow. I won’t claim that it doesn’t lag under certain conditions, but if you run the app I think you’ll agree that it works remarkably well given all the piping necessary. And in any event, if you think there is a lot of circuitry involved at this point, wait until you see how we’re going to create the thousands of individual cells that make up the model animation.

The last two controls to be discussed are Menu and StatusBar. Both of these are containers, a type of ItemsControl. An ItemsControl is a control that can contain ContentControls. In this case the Menu can have children of type MenuItem, and type Separator. MenuItems have a Header property that contains their content (text, icons, images), and can fire a number of events that assist you in reacting to user input on the menu. To create submenus you simply nest MenuItems under a parent MenuItem. Pretty slick. I put my Menu in a nested DockPanel so that I could dock it to the left, and dock a button and text control to the right. If you run the app or look at the window in the designer you’ll see how these are used. It’s possible to put Buttons and other content controls into a MenuItem, but I wasn’t able to get control over the position of topmost MenuItems in the Menu control. You could create two menus and dock them to opposite sides, and in fact it really doesn’t matter how many Menus you use to create the layout you want. StatusBar is very similar to Menu, in that it contains a list of children of type StatusBarItem. As with MenuItem you can place ContentControls in the content property of StatusItems. I won’t dig into the detail because I think it is pretty evident from the source how this is layed out to get the information I want onto the status bar.

So that’s how we can work with XAML to visualize and create a screen layout from controls that either contain content, or lists of other controls. I also said earlier that you can visualize a well-formed set of XAML delarations hierarchically, and in fact this isn’t just conceptual. When your XAML is compiled it is pre-tokenized into a binary stream called BAML, and stored in the resources section of your assembly. At runtime the BAML is parsed and a tree of WPF objects is created. The compiler also generates source (C# in this case) that corresponds to the tokenized BAML, although it isn’t used with the default build settings. If you look in the obj\debug (or \release) project directory after building the assembly you’ll see a file called ALMainWin.g.cs. The .g stands for generated. This file is the C# equivalent of the parsed XAML. It contains the partial definition of class ALMainWin, and declares instances of all of our controls, as well as code for wiring up all the events.

Up to now I have ignored the Application object, which owns all the other objects in the program. I mention it because one of the things that is important about the object tree is the flow of information up and down it. As you’ll see in the next section when we look more closely at controls, resources, and styles, various mechanisms that are used extensively in WPF operate up and down the parent-child tree of controls. These include runtime events, which can “bubble” up the tree or “tunnel” down it, and searches for resources and data sources, which generally bubble up the tree. When a search or event is bubbling up the topmost object is the Application object. When it is tunneling down the bottom is the layer of controls at the “leaf nodes” of the tree. I should also note that this illustration is an idealized representation of the objects in the XAML declaration I used as an example here. If you look in the generated code it appears some objects are optimized away. You won’t find instances of Menu or StatusBar, for example, or StatusBarItems, but you will find all the MenuItems, and the content controls that are in the StatusBarItems. I’m not sure if the behavior of these objects can be entirely inferred from the settings on the children, or they are being created through default behavior elsewhere in the framework. Probably the latter, but if anyone knows for sure drop me a note and I will update this article.

The last thing I want to mention before I move on are cohabiting namespaces. In Windows Forms applications a main window or a dialog derives from System.Windows.Forms.Form. Elements that can be incorporated into a Windows Forms GUI occur in the Forms namespace, such as System.Windows.Forms.Button. WPF’s GUI elements extend from the System.Windows namespace, where you will find a Controls namespace, in which lives… you guessed it: Button. So in addition to System.Windows.Forms.Button there is a System.Windows.Controls.Button. The WPF architects at Microsoft wanted to retain the capabilities of Windows Forms for those who need them, so the two frameworks exist alongside each other. There are a lot of areas where the same semantic element exists in both. Take the graphics primitives like Brush. There is a System.Drawing.Brush, and there is a new System.Windows.Media.Brush that is used by the WPF. The former renders using the GDI+ infrastructure, while the latter uses the new WPF rendering engine that sits on Direct3D. The two Brush types are not compatible. Nor are the two Button types. That doesn’t mean you can’t mix WPF and Windows Forms components and logic into your application; you can, and in some cases you have to. For example there is no color picker dialog in the WPF hierarchy. You can go ahead and use System.Windows.Forms.ColorDialog, but the Brush you get back from it is not the Brush you need to paint in WPF. It has to be converted. For the most part all this means is that you need to be careful about namespaces. MouseEventArgs, for example, occurs in System.Windows.Forms, as well as in the new System.Windows.Input. These issues can be a little frustrating, and will definitely encourage you to program pure WPF or pure Forms as much as possible, but I can understand Microsoft’s goal here.

That’s a pretty good overview of how XAML functions and results in the structure of AvalonLife’s main window. It certainly is no more than an overview. XAML and all the ways that it can be used is a pretty big subject. You can use it to describe fairly standard Windows-style applications like this one, or page-oriented wizard applications, or even a new type of application that deploys over the net and runs in Internet Explorer. Hopefully the examples here are enough to get you moving, and you will find there is a wealth of information available online. People have been probing and explaining XAML since at least 2004, and now that it is here and working I think we’ll see a great many more articles like this one. Speaking of this one, in the next section I’ll go into more detail about controls, and how resources and styles are used to affect the way they appear and behave. With that out of the way we can get to the fun stuff: animating the Game of Life in WPF.

Post navigation

Your email address will not be published. Required fields are marked *

Comment

Name *

Email *

Website

Search for:

About the Author

Mark is a programmer and system architect with nearly two decades of experience. He typically works with small teams of talented developers on next-generation responsive web and mobile applications. Over the years he has built production systems in BASIC, Pascal, C, C++, C#, Python, Java, and javascript ... (more)