Welcome to Pete Brown's 10rem.net

First time here? If you are a developer or are interested in Microsoft tools and technology, please consider subscribing to the latest posts.

You may also be interested in my blog archives, the articles section, or some of my lab projects such as the C64 emulator written in Silverlight.

(hide this)

Getting Started with WPF : Hello World in multiple flavors

Pete Brown - 09 February 2010

So you’re brand spanking new to WPF. You have to start somewhere, so how about good old “Hello World”.

Tooling

First, some tooling. If you are in a situation where you can use Beta or RC products, download Microsoft Visual Studio 2010 RC. I’m still using the Beta, as the RC came out today and doesn’t yet support Silverlight 4.

If you can’t use a Beta or RC, use Visual Studio 2008. Some caveats: many in the community have lamented the state of the WPF design surface in 2008, so it may not be very usable to you outside of typing in plain xaml. Also, VS2008 can only work with WPF 3.5sp1; it won’t work with WPF4. VS2010 can run with both.

My examples below will be in VS2010 with WPF 4, but you can follow along in either. You can get Visual Studio 2010 here.

Creating the Project

Visual Studio 2010 presents you with the Start page when you open it up. This page provides quick access to recently opened projects, as well as information on how to get started in various technologies.

image

After you close the start page, you can always get back to it by choosing View->Start Page

image

Select the “New Project” item from the left of the Start Page (or File->New->Project). You’ll get the “New Project” dialog

image

The New Project dialog has a lot going on in it, so let’s take a moment to explain what’s there. On the far left, you’ll see all the different types of applications your current installation of VS is able to build. Depending upon the edition you are using, and the add-ons you have installed, this list will vary.

I generally code in C#, so I’ll pick the “Windows” category under C#. The middle pane fills up with all the project templates for Windows application development. The framework selection drop down (top left in middle pane) shows all the different frameworks my installation is able to build against. I like the latest and greatest, so I’m using .NET Framework 4 (currently in RC – beta 2 in use in my example)

The right pane shows the description of what you’ve selected in the middle pane. We’ll get to the bottom pane in a moment.

In the middle pane, select “WPF Application”. This is a Windows Presentation Foundation windows application. The other templates are for other WPF supporting libraries, console applications, windows service, and windows forms applications.

Once you select the type of application you want to build, you can then give it a name. I’ll call mine “PeteBrown.WpfHelloWorld”. I typically prefix projects with the company name or my own name, as appropriate.

I usually keep the solution name set to something that makes sense solution-wide. Since we’re doing a simple Hello World, you can leave it as-is.

image

Hit OK to create your project. Visual Studio will take the templates and the parameters you’ve supplied (name, location, solution name) and build out the solution for you. The end result will look something like this:

image

For this example, we’ll deal primarily with MainWindow.xaml

Startup Window

The Main Window is the window that is, by default, displayed when your application starts up. What determines that? It’s the entry in App.xaml

<Application x:Class="PeteBrown.WpfHelloWorld.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
         
    </Application.Resources>
</Application>

If you change the name of your main window, you’ll want to change it there as well.

Main Window

MainWindow.xaml, as mentioned above, is the main window for your application. You can add as many windows as you want, but we’ll do our Hello World example using just the single window.

The default content for MainWindow.xaml is a Window definition with an empty content grid. The Grid is one of many types of panels. It is one of the most powerful panels because it supports table-like subdivision (via ColumnDefinition and RowDefinition elements) as well as automatic stretching of content.

<Window x:Class="PeteBrown.WpfHelloWorld.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <!-- This is where your content goes -->
    </Grid>
</Window>

The first thing we want to do is change the title of the window. Where it says Title=”MainWindow”, replace that to say Title=”My First WPF Application”. You can do that either directly in the xaml, or via the property pane in Visual Studio.

image

As a straight “hello world” would make for a short post with little to teach, I thought we’d take a few different approaches.

Hello World 1: All in Xaml

The basic element you’ll use for displaying text on the screen is the TextBlock. In the grid tags, add this line of markup:

<Window x:Class="PeteBrown.WpfHelloWorld.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="My First WPF Application" Height="350" Width="525">
    <Grid>
        <TextBlock Text="Hello World!" />
    </Grid>
</Window>

The resulting window, in the designer, will look like this:

image

Cool, but I think it would look nicer if we made it a larger font and centered it in the window:

<Window x:Class="PeteBrown.WpfHelloWorld.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="My First WPF Application" Height="350" Width="525">
    <Grid>
        <TextBlock Text="Hello World!"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   FontSize="50"
                   FontWeight="Bold"/>
    </Grid>
</Window>

image

How about we do some more things with this? Maybe twist it around a little

Hello World 2: Transformed

Sure, that was easy to do, but how about we rotate and scale it a little?

<Window x:Class="PeteBrown.WpfHelloWorld.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="My First WPF Application" Height="350" Width="525">
    <Grid>
        <TextBlock Text="Hello World!"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   FontSize="50"
                   RenderTransformOrigin="0.5,0.5"
                   FontWeight="Bold">
            <TextBlock.RenderTransform>
                <TransformGroup>
                    <ScaleTransform ScaleX="1.25"
                                    ScaleY="4" />
                    <RotateTransform Angle="-15" />    
                </TransformGroup>
            </TextBlock.RenderTransform>
        </TextBlock>
    </Grid>
</Window>

image

Pretty neat, eh? We’re doing a few interesting things in this example. First, we added the RenderTransformOrigin property to the TextBlock declaration. That says that any render transforms (like scale, rotate etc.) should be centered on the textblock (0.5, 0.5 is center on a 0 to 1 scale)

Secondly, we opened up the TextBlock tag so it could contain nested tags and added a TransformGroup. What’s with that <TextBlock.RenderTransorm> syntax, though? That’s simply the expanded property element synax. Each property of an element can be written that way. Some, like Text, for example, can also be written as a simple attribute (Text=”Foo”). When you need to nest other elements, you need to use the expanded property element syntax.

Inside the RenderTransform property, we added a TransformGroup. If you were only doing a single transform, you wouldn’t need a group. We’re doing two transforms, though, so we need to enclose them in a group.

Finally, inside the TransformGroup, we added a ScaleTransform and a RotateTransform. Scale is set to 1.0 is the natural size of the element; anything higher is bigger, anything lower is smaller.

That’s it for monkeying around. Let’s keep going.

Hello World 3: All in Xaml, with Scaling

Starting back at our TextBlock, without render transforms, let’s working on scaling it. One way to scale content in WPF is to use a ScaleTransform as we saw above. Another way to handle scaling with little to no effort on your part is to use a ViewBox.

A ViewBox scales content to take up all available space while (by default) retaining its aspect ratio.

<Window x:Class="PeteBrown.WpfHelloWorld.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="My First WPF Application" Height="350" Width="525">
    <Grid>
        <Viewbox>
            <TextBlock Text="Hello World!"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center"
                       FontSize="50"
                       RenderTransformOrigin="0.5,0.5"
                       FontWeight="Bold">
            </TextBlock>
        </Viewbox>
    </Grid>
</Window>

image

Let’s run it and resize the window to see what it looks like

image

image

image

Pretty nice. Our content was smoothly scaled and we didn’t have to do a thing other than include the ViewBox element. This works with more than just text, you can use it for entire user interfaces. Rather than, say, run a visually impaired person’s monitor at 800x600 and have them look at all that blocky low-res UI all day, you can design your app to scale up and provide a very smooth high-DPI version suited for those users. The result is night and day.

So what does ViewBox do? It uses a ScaleTransform on all contained content.

Hello World 4: Set from Code-Behind

For this next example, we’ll add a button to our window, so we’ll need to play with the markup a little. We’ll keep the ViewBox just to show that you can continue to scale the content, but we’ll nest a grid inside it and add a couple buttons.

<Window x:Class="PeteBrown.WpfHelloWorld.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="My First WPF Application" Height="350" Width="525">
    <Grid>
        <Viewbox>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                
                <TextBlock x:Name="GreetingText"
                           Grid.Row="0"
                           Text="Hello World!"
                           HorizontalAlignment="Center"
                           VerticalAlignment="Center"
                           FontSize="50"
                           RenderTransformOrigin="0.5,0.5"
                           FontWeight="Bold">
                </TextBlock>

                <TextBox x:Name="FirstName"
                         Grid.Row="1"
                         Text="Pete"
                         FontSize="25"
                         Margin="10"
                         Height="45" />

                <Button x:Name="GreetMeButton"
                        Width="100"
                        Height="45"
                        Grid.Row="2"
                        HorizontalAlignment="Center"
                        VerticalAlignment="Bottom"
                        Content="Greet me!" />
            </Grid>
        </Viewbox>
    </Grid>
</Window>

Here are the changes made to the window contents

  • Added three rows to the grid (Grid.RowDefinitions). They are set to Auto size, so they’ll be as tall as they need to be to show the content
  • Added a x:Name to each control. When you use x:Name, you can refer to your controls by name in the code-behind.
  • Added a TextBox named FirstName
  • Added a Button named GreetMeButton
  • Set the Grid.Row attached property for each of the controls so they go into the proper grid row (Grid.Row is zero-based)

Here’s what the result looks like in the designer:

image

Now we need to add some code. The idea is, you type your name into the name field, hit “Greet Me” and the text will change to say “Hello, [Name]!” like “Hello, Pete!”

Double-click the button in the designer to generate an event handler for the button click.

<Button x:Name="GreetMeButton"
        Width="100"
        Height="45"
        Grid.Row="2"
        HorizontalAlignment="Center"
        VerticalAlignment="Bottom"
        Content="Greet me!"
        Click="GreetMeButton_Click" />

There are a few other ways you can go about this, like typing Click=” in the xaml and letting Visual Studio generate the handler and markup, or by manually wiring up the event handler in the code behind.

private void GreetMeButton_Click(object sender, RoutedEventArgs e)
{
    if (!string.IsNullOrWhiteSpace(FirstName.Text))
    {
        GreetingText.Text = 
            string.Format("Hello, {0}!", FirstName.Text);
    }
}

The code simply does an assignment of the GreetingText (TextBlock) Text property to be the string specified in the format statement. Note the guard code that first checks to see if the string is empty. The IsNullOrWhiteSpace function is new to .NET 4.

Run it, type your name into the textbox and click the button.

image

An interesting side-effect of using a ScaleTransform on the whole thing and not constraining the size of the content inside the transform is that if you type a really long name, the UI will shrink to accommodate it.

image

Next step is to do this with control-to-control (UI) binding.

Hello World 5: UI Binding

What if we could do the same thing without requiring any code? WPF (and Silverlight) provides the ability to bind the value of a property on one control to a property on another. As is always the case with binding, there are some restrictions: notably that the property that is the binding target (the property getting the value) needs to be a dependency property. If it isn’t, and you try to bind, you’ll get a runtime error.

Taking the same markup from our last example, we’ll remove the Click=GreetMeButton_Click” event handler wireup from the button and add in the binding statement. Removing the handler isn’t strictly necessary, but I want to make sure the value is coming from binding and not have too many pieces in play.

Then update the TextBlock’s Text property to get its value from binding:

<TextBlock x:Name="GreetingText"
           Grid.Row="0"
           Text="{Binding Text, ElementName=FirstName, StringFormat='Hello, \{0\}!'}"
           HorizontalAlignment="Center"
           VerticalAlignment="Center"
           FontSize="50"
           RenderTransformOrigin="0.5,0.5"
           FontWeight="Bold">
</TextBlock>

The binding statement says “From the element with x:Name FirstName, take the value of the Text property and insert it as {0} in the String Format “Hello, {0}!”. The {} have to be escaped with a \ because they are also the opening and closing symbols for markup extensions, of which binding is one.

Run the application and notice how the UI updates in real-time based on what you type in the textbox. You can use this same concept to provide things like character counts (by binding a textblock to the .Text.Length property). It also works well to do things like bind textboxes to sliders to display the numeric value, bind sliders to rotation and scale transforms etc.

As powerful as UI binding is, more often, you’re going to be doing data binding to other classes.

Hello World 6: “Data” Binding

I use “Data” in quotes because I think data binding conjures up bad memories of recordsets, open table cursors, and vcr controls for many of us. Trust me, in WPF (and Silverlight) binding is the way you’ll get almost all your UI –>data->UI updating and display done. If you find yourself writing a lot of someUIELement.Text = myObject.LastName type code, you’re probably doing it wrong.

If you’re looking for one thing to spend time learning inside and out in WPF, it’s binding.

I won’t get into specific patterns here (like ViewModel/MVVM, MVP, SC etc.) but I’ll show how to create a data class to hold our name, and use that in binding.

First, the class to hold the info. I’ll add a couple extra properties just to make it look normal, but we’ll only use FirstName for now.

Add a new class (named Person) to your project.

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Next, in the code-behind for our window, let’s create an instance of the Person class and assign it as the “Data Context” of the window. The DataContext property is the root of all binding. All binding statements will be relative to it. Typically DataContext is a high-level object like a Customer, or in the case of the ViewModel pattern, the ViewModel itself.

public MainWindow()
{
    InitializeComponent();

    Person person = new Person() 
        { 
            LastName = "Brown", 
            FirstName = "Pete" 
        };

    this.DataContext = person;
}

Next, we need to bind the TextBox so that it displays and updates the FirstName property of the Person object that is our DataContext:

<TextBox x:Name="FirstName"
         Grid.Row="1"
         Text="{Binding FirstName, Mode=TwoWay}"
         FontSize="25"
         Margin="10"
         Height="45" />

We want the textbox to both read and write to the FirstName property, so we set the binding mode to TwoWay. If you leave that out, it will typically default (depends on the property) to OneWay, which means effectively a read-only binding.

Next, we need to update the TextBlock to display the value from the FirstName property. We’ll keep the string format intact, but remove the element binding.

<TextBlock x:Name="GreetingText"
           Grid.Row="0"
           Text="{Binding FirstName, StringFormat='Hello, \{0\}!'}"
           HorizontalAlignment="Center"
           VerticalAlignment="Center"
           FontSize="50"
           RenderTransformOrigin="0.5,0.5"
           FontWeight="Bold">
</TextBlock>

The textblock’s binding doesn’t need to specify TwoWay since it will be read-only. It also doesn’t need to specify OneWay since that is the default. The StringFormat is the same one we had earlier, and the same format used when we had code-behind.

Like the previous example, other than the creation of the Person object and setting DataContext, this example has no code-behind. Similarly, the button is unnecessary. What you may notice, though, is unlike the previous example, this one only updates when you tab off the field. Typically, that’s the behavior you’d want (you don’t want to update a business entity with every keystroke), but you can control that.

The UpdateSourceTrigger (which defaults to LostFocus) can be set to PropertyChanged to invoke the binding update with each keystroke.

<TextBox x:Name="FirstName"
         Grid.Row="1"
         Text="{Binding FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
         FontSize="25"
         Margin="10"
         Height="45" />

Great, now what if we want to get both the First and Last names into that greeting text?

Hello World 6.1: Multi Binding

Multi-binding is something that is not used often in WPF, but when you need it, you need it. Multi-binding provides a way to combine the output of two or more source properties into a single target property. In our case, we’re going to use it to combine first and last name in the greeting text.

Back to some UI updates. We now want to capture both first and last name in textboxes, so we’ll get rid of our button and add a Last name text box in its place. The resulting UI xaml looks like this:

<Window x:Class="PeteBrown.WpfHelloWorld.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="My First WPF Application" Height="350" Width="525">
    <Grid>
        <Viewbox>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />

                </Grid.RowDefinitions>
                
                <TextBlock x:Name="GreetingText"
                           Grid.Row="0"
                           Text="{Binding FirstName, StringFormat='Hello, \{0\}!'}"
                           HorizontalAlignment="Center"
                           VerticalAlignment="Center"
                           FontSize="50"
                           RenderTransformOrigin="0.5,0.5"
                           FontWeight="Bold" />

                <TextBox x:Name="FirstName"
                         Grid.Row="1"
                         Text="{Binding FirstName, Mode=TwoWay}"
                         FontSize="25"
                         Margin="10"
                         Height="45" />

                <TextBox x:Name="LastName"
                         Grid.Row="2"
                         Text="{Binding LastName, Mode=TwoWay}"
                         FontSize="25"
                         Margin="10"
                         Height="45" />

            </Grid>
        </Viewbox>
    </Grid>
</Window>

That’s our starting point. Notice that both textboxes are wired up as required. They both have names, but that’s not actually necessary since we’re never referring to them in code. Speaking of code, our code is the same as the previous example – just setting the DataContext to an instance of the Person class.

Before we get into multi-binding, let’s take a quick detour into the Property Element syntax for regular binding. The curly brace syntax we’ve been using so far is the nice markup extension syntax. That makes it quick and easy to write binding statements without having to break everything out. VS2010 even includes intellisense for the extension.

However, we could have written the binding like this:

<TextBlock x:Name="GreetingText"
           Grid.Row="0"
           HorizontalAlignment="Center"
           VerticalAlignment="Center"
           FontSize="50"
           RenderTransformOrigin="0.5,0.5"
           FontWeight="Bold">
    <TextBlock.Text>
        <Binding Path="FirstName"
                 StringFormat="Hello, {0}!" />
    </TextBlock.Text>
</TextBlock>

While more verbose, it is equivalent to this:

<TextBlock x:Name="GreetingText"
           Grid.Row="0"
           Text="{Binding FirstName, StringFormat='Hello, \{0\}!'}"
           HorizontalAlignment="Center"
           VerticalAlignment="Center"
           FontSize="50"
           RenderTransformOrigin="0.5,0.5"
           FontWeight="Bold" />

Now that you know how to break out the binding statement, it’s time for multi-binding. We’re going to use multi-binding to get FirstName and LastName into our textblock.

<TextBlock x:Name="GreetingText"
           Grid.Row="0"
           HorizontalAlignment="Center"
           VerticalAlignment="Center"
           FontSize="50"
           RenderTransformOrigin="0.5,0.5"
           FontWeight="Bold">
    <TextBlock.Text>
        <MultiBinding StringFormat="Hello, {0} {1}!">
            <Binding Path="FirstName" />
            <Binding Path="LastName" />
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>

Note the stringformat is on the MultiBinding itself, and it respects the position of both of our binding values, passing them in as {0} and {1}. Very slick.

If you run the application now, you’ll see something like this:

image 

Where to go from here?

I hope that gives you a taste of how to do some basic things in WPF. There’s a lot more to learn, but once you get the basics of UI structure, event handling, and binding down, everything else is just details :)

Check out http://windowsclient.net/learn for lots of great tutorials. Visit the forums, try the sample applications, and of course, subscribe to my blog and follow me on twitter.

 

Have fun!

     
posted by Pete Brown on Tuesday, February 9, 2010
filed under:      

18 comments for “Getting Started with WPF : Hello World in multiple flavors”

  1. James Towellsays:
    Great. I've seen too many introductions that just show "how to do it", but without the explanation. I'm a programmer who likes to really understand what's going on, and you've explained perfectly. I can now get the next resource with confidence.

Comment on this Post

Remember me

5 trackbacks for “Getting Started with WPF : Hello World in multiple flavors”

  1. uberVU - social commentssays:
    This post was mentioned on Twitter by Pete_Brown: Blogged: Getting Started with WPF : Hello World in multiple flavors (includes binding/multibinding) http://bit.ly/chm8pa
  2. Twitter Trackbacks for POKE 53280,0: Pete Brown's Blog : Getting Started with WPF : Hello World in multiple flavors [irritatedvowel.com] on Topsy.com says:
    <p>PingBack from <a href="http://topsy.com/tb/bit.ly/chm8pa" rel="nofollow" target="_new">http://topsy.com/tb/bit.ly/chm8pa</a></p>
  3. ASPInsiderssays:
    Here's a great post from Pete Brown about getting started with WPF: http://community.irritatedvowel.com/blogs/pete_browns_blog/archive/2010/02/09/Getting-Started-with-WPF-_3A00_-Hello-World-in-multiple-flavors.aspx
  4. Community Blogssays:
    This is Windows Client Developer roundup #11. The Windows Client Developer Roundup aggregates information
  5. Community Blogssays:
    This is Windows Client Developer roundup #11. The Windows Client Developer Roundup aggregates information