You are on page 1of 23

Expression Blend For Developers

Building Silverlight 2 applications just got a lot easier.

A Note on This Tutorial. The history of the material for this tutorial is that Scott Guthrie wrote a
terrific introduction to this material at the end of February, which he gave me permission to turn
into a series of videos, currently (or soon to be) available on Silverlight.NET. This tutorial
completes the circle by building on the videos and integrating the material into the Silverlight
Tutorial series.

The project we're setting out to build is very similar to the Silverlight chat service built by
ScottGu, and is shown in Figure 5-1

Figure 5-1. Completed Application

Getting Started
The best way to get started is by opening Expression Blend.

The first thing you'll see is the New Project window. Choose Silverlight 2 Application, set your
language to Visual Basic and choose a location for your new project, which we'll name
BlendForSilverlight, as shown in Figure 5-2
Figure 5-2. Creating the New Project

The Solution Files

Take a look in the upper right hand corner. The solution, project and other files created by Blend
are identical to the files created by Visual Studio 2008. In fact; there is no exporting/importing
between the two; you can open both development environments on the same files simultaneously
and they will both work perfectly (see Figure 5-3)

Figure 5-3. Blend Files

A Very Quick Tour


The upper right hand corner not only contains the files, but also contains the properties window,
the resources window and, below these, the data window. The central area has the "Art Board"
(the design surface" and the Xaml (markup) which can be split as shown in Figure 5-4

Figure 5-4. Design Surface in Split Mode

I've added highlighting in the upper right hand corner to point out the controls that allow you to
switch among design, Xaml and split mode.

The upper left hand corner contains the Interaction panel and the toolbar. Many of the tools in
the tool bar have a small mark in the lower right hand corner indicating that they can be
expanded to find related tools. For example, expanding the grid tool displays all the layout
controls as shown in Figure 5-5
Figure 5-5. Expanding the Grid Tool

The Chevron tool allows access to all the controls, including any user controls, custom controls
or third party controls (be sure to click Show All) as shown in Figure 5-6

Figure 5-6. Chevron Expanded

Note that from within the Asset Library exposed by expanding the Chevron you can also search
for the control you want, and by clicking on the details radio button you can determine which
library a control is in.

Chat Application - First Steps


Begin by switching to design view, and then hold the control key and use the mouse wheel to
zoom out so that you can see the entire grid.

If your mouse doesn't have a wheel, you can zoom by picking a value (or filling in an arbitrary
value) at the bottom of the design window or by using the menu choices View®Zoom In and
View®Zoom Out

You can move the surface by clicking on the hand and then using the mouse to click and drag or
by using the scroll bars.

Take a look at the upper right hand corner of your grid, as shown in Figure 5-7

Figure 5-7. Upper left corner of the grid

Hovering over the upper left hand corner will tell you if you are in grid layout mode or Canvas
layout mode. If you are in canvas layout, clicking will toggle you back to grid layout mode.

You can create rows and columns just by sliding along the margins and clicking where you want
the rows and margins placed, as shown in Figure 5-8

Figure 5-8. Creating a Row using The Mouse

Immediately after I click, an open lock appears above and below my new row line. I'll add two
lines, one for the top row and one for the bottom; thus implicitly creating a middle row, and thus
three locks as shown in Figure 5-9.
Figure 5-9. Creating the bottom row

Note that immediately after the row is created, not only are the locks shown but the cursor
changes shape to indicate that you can easily move, and thus resize the rows.

The open locks indicate that the rows will resize as the browser (inside of which your Silverlight
application will run) is resized. We want the middle row to do so, but we want the top and
bottom row to remain of fixed size, so click on the top and bottom lock, and then click on split
view to examine the effects on the XAML, as shown in Figure 5-10.
Figure 5-10. Middle Row has Star Sizing

Note that (on line 10) the middle row definition has its Height property set to * (star) indicating
that it will take up the remaining room after the other rows are allocated.

Creating the Columns

Let's create two columns, a smaller right column that has a fixed size and a larger floating size
left column. Try this yourself without additional help.

Creating the Background Color

We'd like to give the entire application a light blue background, but to make it a bit more
attractive, we'll use a linear gradient brush. To do this, please follow these numbered steps,
reflected in Figure 5-11.

1. Click on the grid (Layout Root) in the Interaction Panel


2. Click on the properties panel and expose the Brushes subpanel. Click on the Background
brush
3. Click on the Gradient Brush button
4. Add stops along the gradient by clicking and then
5. manipulating the color bar and
6. Moving the color dot for precise color choices

Figure 5-11. Adding a Linear Gradient By the Numbers

Adding A Send Buton


The next step is to add a button to the lower right hand corner. Click on a button on the toolbar,
drag it to approximate size in the area and then click in the properties window.

Next, open the Layout subpanel and set the Width and Height to Auto and set a small value (e.g.,
4) for the Margin, all around. This will fill the area, but with a reasonable margin from the edges
of the column and row.

The value you put in a property box does not take effect until you leave that box (e.g., using tab)

There are a few things to notice about the Layout Panel (Figure 5-12)

Figure 5-12. Setting the Button Layout with the Layout Panel

First, you can set the Width and/or Height to auto by pressing the button to the right of the field.
Second, when you set a specific (non-default) value next to a property, a white dot appears (there
are 5 white dots in Figure 5-12).

Take a look at the Xaml once you have these values set,

<Button HorizontalAlignment="Stretch" Margin="4,4,4,4" VerticalAlignment="Stretch"


Grid.Column="1" Grid.Row="2" Content="Button"/>

Blend has translated your choices into Xaml that will create the look you've chosen. We have,
however, forgotten to change the "Content" property from Button to "Send". The easiest way to
do so is to type Content into the property Search box,

At "co" you will get back "Column", "Horizontal Content", "Data Content" and "Content" as
shown in Figure 5-13
Figure 5-13. Search with "co"

Refine your search just a bit further, and you reduce the complexity a bit, as shown in 5-14,

Figure 5-14. Refined Search


In any case, remove the word Button from the Content field and put in the word Search. Your
text task is to set the font size to 18 and the font family to Verdana. Oh, and set it to Bold. (No
cheating! Do this in design, not in Xaml!) (You'll find the image on the next page)

Some (few) controls have a text property and most have a content property. Those with a content
property often fill that property with text, but they are free to fill it with almost anything else,
including other controls.

Figure 5-15. Setting the Font Family and More


Creating The Rounded Rectangle Input Area
Examining the finished application shown at the top of the tutorial reveals that the input area has
a rounded rectangle as shown in Figure 5-16

Figure 5-16. Input Area is a Rounded Rectangle

This is slightly tricky to achieve since the TextBox does not have a setting for rounding its
corners. It turns out, however, that the Border control does.

Our tricky solution, therefore, is to put a TextBox within a border, and letting the Border handle
the look and feel and the TextBox mange the text handling!

Step one is to fill the grid square with a border. Click on the Grid pallet and expand it to click on
a border, and then drag out the border into the appropriate grid location (we'll size it properly
with the properties window).

Once in the grid, set its Width and Height to Auto, and its Margin to 4 all around, as shown in
Figure 5-17

Figure 5-17. Sizing the Border in the Layout Panel


Let's set the background brush for the border, by setting the values in the Brushes property
window by hand to 255, 255, 255 with an Alpha (opacity) of 45%.I highly recommend doing
these one by one. AS you set the red value the background turns Red. When you set the Green
background to 255 as well, the background turns bright yellow. Add 255 for Blue and the
background turns bright white. Finally, knocking the Alpha down to 45% gives us the pale shade
we're looking for, as shown in Figure 5-18

Figure 5-18. Setting the Background Brush

If you look carefully under the settings for RGBA you'll find the hex value #72FFFFFF which is
the equivalent of setting the four values independently (with the alpha value first).

Our Border control also needs its Border brush set (in 5-18 it is set to "No Brush"). The values
we want (which it may well default to) are Alpha of 100% and full black (00,00,00).

The Border brush won't appear unless we set its thickness (which defaults to zero); which we can
do in the Appearance panel. Let's set it to 2. This is also where we get to set the rounded corners,
which we'll set to 5 as shown in Figure 5-19.

Figure 5-19. Setting the Border Thickness and Rounded Corners

Placing the Text Box

The border has the rounded corners, but as noted above, a border can't accept text input. For that
we need a text box, but we don't want the text box to overwrite the background we just carefully
placed in the border.
Zap! All we need to do is make the text box reside within the border, and set it to transparent!

To place the text box within the border, double click on the border in the Interaction panel,
signaling to Blend that it is now the container for the next control. That is, the next control we
add will not be a sibling, but a child of the border. The yellow rectangle will jump from Layout
Root to the Border as feedback that it is now the active container. When you drag the text box in,
it will be placed inside the text box, as shown in Figure 5-20, both from a design view and a
Xaml view.

Figure 5-20. Text Box Nested within Border

Review of key points

There are a few new concepts all put together in this one construct, so a quick review is in order:

• The border is set with a background color and the rounded corners
• The text box is nested fully within the border, and the text box's background opacity is set
to 0 (making it transparent). That allows the text box to "pick up" the background color
and rounded corners of the border
• In order to ensure that the text box will be nested within the border, double click on the
border in the Interaction panel surrounding the border with a yellow box and thus
indicating that it is now the container.

Creating the Top Row

We'll do the same thing on the top row, except that instead of using a TextBox we'll use a
textblock, setting the text inside to a font family of Comic Sans MS, a font size of 48 and the text
of your own name.

To set the upper right hand corner, begin by double clicking on the LayoutRoot to make it the
container, then click on an Image Control in the Chevron. Set the source property to point to any
image file on your disk.

Creating the Conversation Area


The final part of our IM application is the middle: the conversation area, which will in fact be a
list box. We'll place that in a border that will stretch across both columns. You can place that
manually, just by stretching a border across both columns using the mouse.

You'll notice as you go to refine the placement in the Layout panel that the ColumnSpan has
been set to 2, and all you need do is fix the Margins to make the border fit snugly (e.g., with a
margin of 4 all around.)

You'll want to set the background to a solid color ( FF03588D works nicely) and once again we'll
set the corners to a radius of 5 all around, as well as setting the border to black and a thickness of
2.

Once again, double click on the new border, and drag a list box (from the chevron) inside the
new border, and set it to fill the border.

Adding dummy data

It is convenient to have dummy data to fill the list box, and the easiest way to do that is in Xaml.
This is a good opportunity to switch to Visual Studio, so save all the files in Blend but leave
blend open and switch over to the project in Visual Studio 2008.

If your project was already open, you'll be notified that it has changed, say yes to accept the
changes, and you should find that all the files are shown and your Page.xaml looks impressively
similar to what you just saw in Blend.
Figure 5-21. Examining the Project in Visual Studio

You can now easily populate your list box with dummy list items,

<ListBox Height="Auto" Width="Auto" x:Name="Conversation" >


<ListBoxItem Content="Jesse: Is this working?" />
<ListBoxItem Content="Scott: Of course." />
<ListBoxItem Content="Jesse: I'm following the directions." />
<ListBoxItem Content="Scott: Then you should be fine." />
<ListBoxItem Content="Jesse: Great, thanks." />
</ListBox>

Save your file and switch back to Blend (no need to close Visual Studio 2008). When you do,
Blend lets you know the files were modified,
Figure 5-22. Blend letting you know the files were modified

Click yes, and your dummy data should be visible.

You can press F5 to run the program as an interim check. You'll find that the image doesn't
appear unless you place a copy of the image file in the bin/debug directory as well. That's not a
big problem, however, as we'll be binding to the source for the image file.

Building the Business Layer

To support the exchange of instant messages, we will build (or, to be more honest, stub out) a
service that will identify the remote user's name, image and that will manage the messaging.

Because we are more interested in Silverlight than in building the service (at least for the
purposes of this tutorial) we'll simplify this by creating a ChatSession class that will stand in for
all the networking, sockets and other plumbing, and which will maintain instead a collection of
Message objects. As you can guess, we'll also create, therefore, a Message class to represent each
message "transmitted" by the service.

Because Visual Studio 2008 is better suited to programming than is Blend, we'll do this work in
Visual Studio 2008 but we'll get there by right clicking on the solution in Blend and choosing
Edit In Visual Studio, as shown in Figure 5-23.

Figure 5-23. Moving to Visual Studio from Blend


Once in Visual Studio 2008 right click on the project and choose Add Class. In the new items
dialog select class and name your new class ChatMessage as shown in Figure 5-24

Figure 5-24. Adding the Chat Message Class

The Chat Message class is pretty straight forward, consisting only of two properties,

public class ChatMessage


{
private string privateUserName;
public string UserName
{
get
{
return privateUserName;
}
set
{
privateUserName = value;
}
}
private string privateText;
public string Text
{
get
{
return privateText;
}
set
{
privateText = value;
}
}
}
The ChatSession is somewhat more complex, but we'll simplify greatly by stubbing out all the
difficult parts.

We begin by marking the class as implementing INotifyPropertyChanged, which as noted in the


DataBinding Tutorial ensures that the UI fields will be updated as the data changes.

public class ChatSession : INotifyPropertyChanged


{

This interface requires only that we provide a PropertyChangedEventHandler that implements


the PropertyChanged event,

public event PropertyChangedEventHandler PropertyChanged;

The class has two straight forward public properties: one for the user's name, and one for the
URL of the user's Avitar (image),

private string privateRemoteUserName;


public string RemoteUserName
{
get
{
return privateRemoteUserName;
}
set
{
privateRemoteUserName = value;
}
}
private string privateRemoteAvatarUrl;
public string RemoteAvatarUrl
{
get
{
return privateRemoteAvatarUrl;
}
internal set
{
privateRemoteAvatarUrl = value;
}
}

Next we want to create a collection of the ChatMessages. What we want, however, is to raise an
event each time the collection changes (e.g., a message is added) and we want the list box to
respond to that event. We could create all of that, but the framework makes it easy. Listbox
already knows to respond to PropertyChanged events, and there is a special collection type
ObservableCollection(of) that fires exactly that event every time the collection is changed. This
makes life a walk in the park,

private ObservableCollection privateMessageHistory;


public ObservableCollection MessageHistory
{
get
{
return privateMessageHistory;
}
internal set
{
privateMessageHistory = value;
}
}

Sending a message is now as simple as adding a new Chat message where the text is taken from
the message box and the user name is taken from the current user's name (for now, hard coded to
"Me")

public void SendMessage(string message)


{
// Todo: Send to remote chat server over sockets
MessageHistory.Add(new ChatMessage {Text = message, UserName = "Me"});
}

We leave it as an exercise for the reader to actually send the message across the network

Before we can send messages, however, we must connect with the remote user, obtaining the
remote user's name, avatar and initializing the observable collection of messages,

public void ConnectWithRemoteUser(string remoteUserNameParam)


{
// Todo: 1) Wire-Up socket stack to receive notifications of received
messages
// 2) Wire-Up to a remote avatar service instead of hard-coding
my picture

RemoteUserName = remoteUserNameParam;
RemoteAvatarUrl = "billg.jpg";
MessageHistory = new ObservableCollection();

// Notify any listeners of property changes


if (PropertyChanged != null)
PropertyChanged(this, new
PropertyChangedEventArgs("RemoteUserName"));
if (PropertyChanged != null)
PropertyChanged(this, new
PropertyChangedEventArgs("RemoteAvatarUrl"));
if (PropertyChanged != null)
PropertyChanged(this, new
PropertyChangedEventArgs("MessageHistory"));
}

Finally, we'll add a method we can call that will simulate an exchange of IM's (but we'll only call
this method when we're in design mode; not when the program is actually running)

public void PopulateWithDummyData()


{
RemoteUserName = "BillG";
RemoteAvatarUrl = "billg.jpg";

MessageHistory = new ObservableCollection();


MessageHistory.Add(new ChatMessage {UserName = "BillG", Text = "How is
your video going?"});
MessageHistory.Add(new ChatMessage {UserName = "BillG", Text =
"Hmmm....you there?"});
MessageHistory.Add(new ChatMessage {UserName = "BillG", Text =
"Hello???"});
MessageHistory.Add(new ChatMessage {UserName = "Jesse", Text = "Sorry
Bill - working on a video..."});
MessageHistory.Add(new ChatMessage {UserName = "BillG", Text = "Oh -
ok."});
}

To ensure that this dummy data is in place during design mode, we'll test in the constructor of
ChatSession,

public ChatSession()
{
if (HtmlPage.IsEnabled == false)
{
PopulateWithDummyData();
}
}

DataBinding

With these classes in place (you did build to make sure all is well, yes?) we're ready to data bind.
Let's do that in Blend (!)

Make sure you've saved all your files in Visual Studio 2008 but don't close it, just switch over to
Blend. Say yes to the "something has changed when I wasn't looking" dialog (your wording may
vary). Under the project panel you should see a Data panel, within which is a tab marked CLR
Object. Click on that and Hey! Presto! You will see Blend for Silverlight that will open up to
show a dialog that lets you add any CLR object as a data source. Notice that ChatMessage and
ChatSession are listed. Click on Chat session and click OK and Chat SessionDS appears in your
Data list, as shown in Figure 5-25.
Figure 5-25. Adding Chat Session as a Data Source

Now, this is really scary how easy this is; click on RemoteUserName, and drag it onto the name
field at the top of the design and let go, as shown in Figure 5-26

Figure 5-26. Binding the RemoteUserName to the TextBlock by Drag and Drop

When you let go, Blend recognizes that you are binding the data, and asks first which field you'd
like to bind to (guessing you'd like to bind to Text) and then offers (if you expand the dialog)
additional options, such as two-way binding (so that if the UI is updated, the changes will be
written back to the data source), as shown in Figure 5-27
Figure 5-27. Creating the Data Binding

When you click OK the UI is immediately updated with the bound data.

Figure 5-28. Bound Data

Unfortunately, your list box does not know how to display a Chat Message (why would it?). We
can fix that, of course, by using a Data template,

<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock FontFamily="Comic Sans MS" FontSize="16"
Foreground="red" Text="{Binding UserName}"/>
<TextBlock Text=": "/>
<TextBlock FontFamily="Comic Sans MS" FontSize="16"
Text="{Binding Text}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
This teaches the ListBox how to display each of the list items, specifying that each will be
dislayed with three TextBlocks, aligned horizontally by a stack panel, as shown in Figure 5-29

Figure 5-29. Data Template at work

Adding Templates
The Listbox and especially the Start Button don't quite look the way we'd like. In the previous
version of this tutorial we fixed them with hand-written templates, but Blend now offers a far
better solution, which we will introduce in the next tutorial, so we're going to leave this as it is
for now (not quite done, but a good way towards a solid introduction to using Blend and Visual
Studio together for programming an application).

Better not to finish, than to show you what amounts to an obsolete and inferior way of solving
the problem. In the interim, both Tim Heurer and I and many others will be blogging about the
new approach to Templates, and we'll have the full tutorial out within a few weeks.

You might also like