We continue with our basic “hello world” WPF application by adding a button to our main window and then building and running the application. We also talk about the difference between forms in Windows Forms and windows in WPF, as well as how to add event handlers.
I want to insert a caveat at this point. These first few “hello world” posts are basic—very, very basic. Adding a button to a form and having it display a message box is what most of us do in the first five minutes that we spend playing with a new language or framework. So don’t expect any cosmic secrets here. I just want to take a little time to throw together a super simple application and then comment a little bit on what I’m seeing.
Form vs. Window
Let’s start by just building our basic wizard-generated application and then running it. I’ll continue doing parallel stuff in a Windows Forms application, so we can compare the two. Here’s what we get when we run the applications:
Nothing too earth-shattering here, although WPF has gotten rid of two old standbys that I’m sick of—the little multi-colored default application icon and the battleship grey form background. Good riddance to both of them.
In both cases, we get a simple window with the standard window decoration elements. Nothing appears to have changed here. But if we look at the type that implements the window in either case, we see that everything is different under the covers.
Win Forms is using a System.Windows.Forms.Form (System.Windows.Forms.dll), while WPF’s main window is a Systems.Windows.Window (PresentationFramework.dll).
I’m curious, so let’s compare the two classes briefly. (If you don’t already know about it, now is a good time to teach yourself Ctrl-Alt-J in Visual Studio for popping up the Object Browser).
The inheritance tree for a Win Forms Form is:
And the inheritance tree on the WPF side, for the Window, is:
(I included MSDN’s basic description of each class). We won’t go any deeper than this for now, but the point is that, for WPF, things are very different under the hood.
One difference to note is that WPF does not support MDI (Multiple Document Interface), whereas Windows Forms does. I could see a case for continuing to support MDI functionality for those who need it, but I can also see why it’s not worth carrying the old MDI framework forward. It’s rare to see applications that support MDI in exactly the way that Win Forms supported it (windows entirely contained within parent window, etc). When you do see a parent window containing child windows, the visual interface is likely different from the traditional sizable child windows—e.g. using a series of tabs. There are so many different ways of doing this that it’s just easier to roll your own mechanism. Or perhaps we could get some support in WPF in the future for a more updated and customizable implementation of MDI.
Another good way to see what goes on behind the scenes for the main form/window classes is to look at their lifecycle, as described by the events that the classes fire. I always end up wanting to keep these “window lifetime” event lists for reference purposes, so they’re worth jotting down here.
Forms.Form events (Win Forms)
Loading/opening new form (application startup), events fired are:
Closing a Win Forms Form, the events that fire are:
Windows.Window events (WPF)
Loading/opening new window (application startup), events fired are:
Closing a WPF Window, the events that fire are:
Adding a Button
Now let’s add our first control to the WPF window in our application. We’ll add a button to the window by just dragging it onto the design surface in the XAML designer.
The designer ends up looking something like this:
And the XAML snippet in the bottom window is also updated as soon as we add the button:
Note that everything we do in the designer is immediately reflected in the XAML. This is because there is an exact match between what the designer renders and what is stored in the XAML. You can think of the designer (or design surface) as nothing more than a combination XAML viewer and XAML editor.
We can also demonstrate here that working in the opposite direction works as expected—if you edit the XAML, the designer updates immediately to reflect your changes. Note that we don’t even have to save the file—the content in the designer changes immediately, as we type! You can also edit property values in the Properties window that is docked to the right of the designer (under the Solution Explorer).
Let’s take a look now at what happens in our generated code, once we have a couple of controls on the design surface. I’ll add a CheckBox to the window and then open up Window1.g.cs. Note that this source file is not updated until we build (since it’s generated from the XAML whenever we build). If we rebuild the project now and take a look, we’ll see that both controls have been declared at the top of our partial class and that the Connect method includes them in its switch statement:
This code is creating/initializing the controls at runtime, based on the content in the BAML memory stream that was included in our assembly.
Now it’s time to wire up our first event handler so that we can do something when the button is clicked.
At first glance, something important is missing from Visual Studio. When we have the WPF Designer open for our main window and have selected our button, the Properties window doesn’t seem to list any events. Entirely missing is the little event icon that lets us get a list of all events for the currently selected control.
The question then becomes—what designer support do we have for adding event handlers in a WPF application? The answer is to edit the XAML directly. If we position the cursor at the end of the attribute list for the Button element in our XAML editor and press space, we see a nice intellisense popup listing all available attributes (properties and events). Note the presence of the Click event in the image below:
If we select the Click event, or start typing “Click”, the editor adds a new attribute for the Click event and the intellisense window changes to indicate <New Event Handler>. At this point, we can dbl-click on <New Event Handler> to generate our event handler, or—better yet—just press the TAB key to generate the handler.
Once we’ve created the default event handler, our XAML looks like this (note the default handler name):
Now we can open our partial class implementation of Window1 in Window1.xaml.cs and we see our empty handler that has been generated for us:
We’re finally ready to add some “hello world” code to our handler, which will execute when the Push Me button is clicked:
And—highly satisfying—we can run our program and get one of two message boxes to display, depending on whether the “verbose” checkbox is checked:
Next time, I’ll start looking in more depth at the various controls available in a WPF application, starting with the Button.