You are on page 1of 34

DataBinding & DataTemplates Using Expression Blend

In Tutorial #2, Data Binding, we explored how to bind relatively simple data to relatively simple
controls. This tutorial will dive a bit deeper, looking specifically at two topics that are of
particular importance in DataBinding:

• Binding complex objects to List controls


• Using Expression Blend vs. Creating bindings in Xaml by hand

While we did have a list of chapter names that we bound to a list box in the previous chapter that
was not quite complex enough to raise the question of DataTemplates; in this tutorial we'll look
at what happens when you bind more complicated objects, that have numerous properties, to a
list control.

The Finished Product

To get an idea of where we are going, and what pieces will be important, let's take a look at some
aspects of the finished product.

Figure 7-1. ListBox with Data From Books

A quick look at Figure 7-1 shows that we're listing each book in a collection, formatting the
name in red and Comic Sans MS, 16 point, while we're formatting the rest of the line, which
includes the Book's ISBN and cover price in Verdana, black, 14 point. How we are doing that is
not yet obvious. Nor is it obvious where the information (book title, ISBN or price) is coming
from.

It gets more interesting when you click on a particular book in the list as shown in Figure 7-2
Figure 7-2. Book Details

When the user clicks on a book it is highlighted, and the list box literally shrinks down to make
room for the details about the book. What is now visible is 6 rows of information about the book,
including a list of authors bound to a smaller list box, a few rows of strings and integers, and a
rating displayed both in a slider and a TextBlock.

Leveraging Expression Blend with Visual Studio

This tutorial will show how to create bindings both to lists and to individual controls using
Blend; giving you more leverage, more quickly, and, like any good development environment,
allowing you to focus on what you are trying to build rather than on the specific syntax of
building it.

This tutorial will not recapitulate the concepts covered in Tutorial #2, but we will introduce a
few new UI concepts that Blend makes easy to implement.

Creating the Project

To get started, create a new project, in Blend, called Silverlight Data as shown in Figure 7-3
Figure 7-3. Creating a new project in Blend

Once the project is created, set the outermost user control to 680 x 550 and create three rows, as
shown in Figure 7-4

Figure 7-4. Creating three rows in Blend

If you are not comfortable with how to create these rows in Blend, you'll want to pause here and
read Tutorial #5 on Expression Blend for Developers
Note that the top row is of fixed height, and most of the space is devoted to the middle row. If
you care to make the measurements exact, feel free to open the Xaml window and set them by
hand, though this is not necessary at all:

<Grid x:Name="LayoutRoot" Background="White" >


<Grid.RowDefinitions>
<RowDefinition Height="90"/>
<RowDefinition Height="200"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

Add the title to the top row, by dragging on a TextBlock, and setting its properties as shown in
Figure 7-5

Figure 7-5. Setting the properties for the Title

Drag a ListBox control into the 2nd Row

Drag a ListBox from the Assets into the Second Row and set its Alignments to stretch and its
margins to 25,50,25,50 as shown in Figure 7-6
Figure 7-6. Adding and Positioning the ListBox Control

Populating the ListBox with hard-coded strings

One way to separate the issue of managing the look and feel of ListBoxItems from that of
binding ListBoxItems is to start by just hard-coding a few values using the ListBoxItem
Collection Editor. This only takes a few minutes; here's how you do it:

Begin by clicking on the ListBox in the Interaction Panel and then click on Item (Collection) in
the Common Properties tab as shown in Figure 7-7

Figure 7-7. Adding to the Items Collection


This opens a rather intimidating Collection Editor. Don't panic. Click on the button which
disingenuously says "Add another item." Again, don't panic when a virtually empty dialog
appears. Click the check box "Show System Assemblies."

You just went from nothing to far too much. You can reduce this clutter and find what you need
by searching; enter ListBoxItem in the search box as shown in Figure 7-8

Figure 7-8. Selecting which class to add to the list box's collection

Clicking on ListBoxItem will open a special editor that will allow you to edit your ListBoxItem's
content, giving you a tremendous degree of control over the appearance of the items you'll be
adding by hand,
Figure 7-9. Property Editor for ListBoxItem

Fill in the properties for your first List Box Item, setting the text to the first line shown in Figure
7-9. Once you've added three or four items, click OK and you'll return to the page, but the list
box will no longer be empty. Run the program and you'll see your hard-coded data displayed in
the list box as shown in Figure 7-10

Figure 7-10. Hard Coded ListBoxItems


Design by Crick And Watson[1]
Our goal is to create an n-tier application, with the three principal layers
being:

• User Interface Layer


• Business Layer
• Persistence Layer

The User Interface (UI) Layer is the part the user sees and interacts with,
and we'll build that in Expression Blend using Silverlight controls.

The Business Layer manages the logic of your application; we'll build that in Visual Studio
using code in C#.

For this application the Business Layer will be encapsulated in three relatively simple classes:

1. Library represents a named collection of books retrieved from a larger collection. The
larger collection might be a database, a web service or some other persistence
mechanism.

2. Book represents some level of abstraction of a Book, though it may represent one or
more printings or editions. It contains such vital properties as the author, the title, the
author, the publisher, the author, the publication date and the author.

3. Author, representing what we need to know about the author of a Book for this
application. Author is so vital to a book we've made it a class of its own.

Our goals in this tutorial will include:

1. Data binding: bind the members of a list box to the Books collection in a Library
2. Data Template: tell the list box how to display (some of ) the properties of each book in
the list

Creating the Data Classes

Begin by deleting the ListBox (and its contents) from the LayoutRoot. (Really, it's okay, I'll
make you a much nicer ListBox with real data; I promise).
Blend isn't the right place to create business layer classes, so click on File-Save all and then right
click on the application and choose Edit in Visual Studio as shown in Figure 7-11.

Figure 7-11. Moving from Blend to Visual Studio to Create Classes

When Visual Studio opens, create the three classes discussed above. The complete code is
provided here, and taken apart immediately afterward.

// Author.cs
public class Author
{
private string privateName;
public string Name
{
get
{
return privateName;
}
set
{
privateName = value;
}
}

// Book.cs
public class Book : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

public Book()
{

public Book(string title, ObservableCollection authorList, double


coverPrice, string isbn10, string publisher, int edition, int printing,
string pubYear, double rating)
{

this.Title = title;

this.Authors = new ObservableCollection();


foreach (Author auth in authorList)
{
this.Authors.Add(auth);
}

this.CoverPrice = coverPrice;
this.ISBN10 = isbn10;
this.Publisher = publisher;
this.Edition = edition;
this.Printing = printing;
this.PubYear = pubYear;
this.Rating = rating;

private string privateTitle;


public string Title
{

get
{
return privateTitle;
}
set
{
privateTitle = value;
NotifyPropertyChanged("Title");
}
}
public string NumAuthors
{
get
{
return privateAuthors.Count.ToString();
}
}
private ObservableCollection privateAuthors;
public ObservableCollection Authors
{
get
{
return privateAuthors;
}
internal set
{
privateAuthors = value;
NotifyPropertyChanged("Authors");
}
}
private double privateCoverPrice;
public double CoverPrice
{
get
{
return privateCoverPrice;
}
set
{
privateCoverPrice = value;
NotifyPropertyChanged("CoverPrice");
}
}
private string privateISBN10;
public string ISBN10
{
get
{
return privateISBN10;
}
set
{
privateISBN10 = value;
NotifyPropertyChanged("ISBN10");
}
}
private string privatePublisher;
public string Publisher
{
get
{
return privatePublisher;
}
set
{
privatePublisher = value;
NotifyPropertyChanged("Publisher");
}
}
private int privateEdition;
public int Edition
{
get
{
return privateEdition;
}
set
{
privateEdition = value;
NotifyPropertyChanged("Edition");
}
}
private int privatePrinting;
public int Printing
{
get
{
return privatePrinting;
}
set
{
privatePrinting = value;
NotifyPropertyChanged("Printing");
}
}
private string privatePubYear;
public string PubYear
{
get
{
return privatePubYear;
}
set
{
privatePubYear = value;
NotifyPropertyChanged("PubYear");
}
}
private double privateRating;
public double Rating
{
get
{
return privateRating;
}
set
{
privateRating = value;
NotifyPropertyChanged("Rating");
}
}

private void NotifyPropertyChanged(string propertyName)


{
if (PropertyChanged != null)
PropertyChanged(this, new
PropertyChangedEventArgs(propertyName));
}
}
}

// Library.cs

public class Library : INotifyPropertyChanged


{
public event PropertyChangedEventHandler PropertyChanged;

public Library()
{
PrivateName = string.Empty;
PrivateBooks = new ObservableCollection();
if (HtmlPage.IsEnabled == false)
{
GenerateDummyData();
}

public void GenerateDummyData()


{

Books = new ObservableCollection();

ObservableCollection Authors = new ObservableCollection();


Author Author1 = new Author();
Author Author2 = new Author();
Author1.Name = "Jesse Liberty";
Author2.Name = "Mark Twain";
Authors.Add(Author1);
Authors.Add(Author2);

Book newBook = new Book("Programming Silverlight", Authors, 49.99,


"0123456789", "O'Reilly", 1, 1, "2000", 5.0);
Books.Add(newBook);

Author2.Name = "Cormac McCarthy";


newBook = new Book("Programming Without Shoes", Authors, 149.99,
"0123456789", "O'Reilly Media", 1, 1, "1955", 4.5);
Books.Add(newBook);

Author2.Name = "Ian McEwan";


newBook = new Book("Programming Without A Spec", Authors, 149.99,
"0123456789", "O'Reilly Media", 1, 1, "1955", 4.3);
Books.Add(newBook);

Author2.Name = "Stephen King";


Books.Add(new Book("Programming Without A Conscience", Authors, 149.99,
"0123456789", "O'Reilly Media", 1, 1, "1955", 4.1));

private string PrivateQuery;


public string Query
{
get
{
return PrivateQuery;
}
set
{
PrivateQuery = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Query"));
}
}

private string PrivateName;


public string Name
{
get
{
return PrivateName;
}
set
{
PrivateName = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}

private ObservableCollection PrivateBooks;


public ObservableCollection Books
{
get
{
if (PrivateBooks.Count < 1)
{
GetData();
}
return PrivateBooks;
}
set
{
PrivateBooks = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Books"));
}
}
public void GetData()
{

Name = "Liberty Books";


ObservableCollection Authors = new ObservableCollection();
Author Author1 = new Author();
Author Author2 = new Author();
Author Author3 = new Author();

Author1.Name = "Jesse Liberty";


Author2.Name = "Tim Heurer";
Authors.Add(Author1);
Authors.Add(Author2);
Book newBook = new Book("Programming Silverlight", Authors, 49.99,
"TBD", "O'Reilly Media", 1, 1, "2009", 5.0);

PrivateBooks.Add(newBook);

Authors.Remove(Author2);
Author2 = new Author();
Author2.Name = "Alex Horovitz";
Authors.Add(Author2);

newBook = new Book("Programming .NET 3.5", Authors, 49.99, "0-596-


51039-X", "O'Reilly Media", 1, 2, "2008", 4.7);
PrivateBooks.Add(newBook);

Authors.Remove(Author2);
Author2 = new Author();
Author2.Name = "Dan Hurwitz";
Author3.Name = "Brian Macdonald";
Authors.Add(Author2);
Authors.Add(Author3);

newBook = new Book("Learning ASP.NET 3.5", Authors, 44.99, "0-596-


51845-5", "O'Reilly Media", 2, 1, "2008", 4.8);
PrivateBooks.Add(newBook);

Authors.Remove(Author2);
Authors.Remove(Author3);
Author2 = new Author();
Author2.Name = "Donald Xie";
Authors.Add(Author2);
PrivateBooks.Add(new Book("Programming C# 3.0", Authors, 44.99, "0-
596-51845-5", "O'Reilly Media", 5, 2, "2008", 4.3));

Authors.Remove(Author2);
Author2 = new Author();
Author2.Name = "Dan Hurwitz";
Authors.Add(Author2);
PrivateBooks.Add(new Book("Programming .NET Windows Apps", Authors,
49.95, "0596003218", "O'Reilly Media", 1, 1, "2003", 3.7));

Authors.Remove(Author2);
Author2 = new Author();
Author2.Name = "Brad Jones";
Authors.Add(Author2);
PrivateBooks.Add(new Book("Teach Yourself C++ in 1 Hour", Authors,
44.99, "0672327112", "Sams", 6, 1, "2008", 2.9));

Authors.Remove(Author2);
Author2 = new Author();
Author2.Name = "David Horvath";
Authors.Add(Author2);
PrivateBooks.Add(new Book("Teach Yourself C++ in 24 Hours", Authors,
34.99, "0672326817", "Sams", 4, 1, "2004", 2.4));

Authors.Remove(Author2);
PrivateBooks.Add(new Book("Programming VB.NET", Authors, 39.95,
"0596004389 ", "O'Reilly Media", 2, 1, "2003", 3.2));
PrivateBooks.Add(new Book("Visual C# 2005 Dev Notebook", Authors,
29.95, "059600799X", "O'Reilly Media", 1, 1, "2005", 3.7));
PrivateBooks.Add(new Book("Clouds To Code", Authors, 41.95,
"1861000952", "Wrox", 1, 1, "1997", 4.1));

}
}

Save the project in Visual Studio and before you return to Blend let's unpack these classes to
make sure they are fully understood.

ObservableCollection and INotifyPropertyChanged

You may have noticed the following declarations,

public class Library : INotifyPropertyChanged


{
//...
private ObservableCollection PrivateBooks;

public class Book : INotifyPropertyChanged


{
//....
private ObservableCollection privateAuthors;

An observable collection is much like a list, except that it implements the


INotifyPropertyChanged event, firing that event each time the collection is modified in any
way). This is done without any effort on your part, and is especially useful because all of the
UIControls have built into them the code to register for that event. If you want your controls to
be updated when the underlying collection changes you do not have to write any code, it all
happens automagically! Right convenient, that.

The Library class consists of a constructor (which just sets the name to empty and allocates
memory for an empty observable collection of Book objects.

It does one more terribly convenient thing, it check to see if we are in design mode, and if so it
generates Dummy data to fill the list box so we can see what things look like. This is entirely
gratuitous but very handy,

if (HtmlPage.IsEnabled == false)
{
GenerateDummyData();
}

The Library constructor is followed by two properties, Name and Books, where the latter is a
very clever observableCollection of Book objects. Clever because when you try to retrieve the
collection it checks to make sure it has Book objects to return and if not it calls a private helper
method GetBooks

public ObservableCollection<Book> Books


{
get
{
if (PrivateBooks.Count < 1)
{
GetData();
}
return PrivateBooks;
}
set
{
PrivateBooks = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Books"));
}
}

GetBooks is free to go off to a web service and execute a query, or to read books from an XML
file or do just about anything it chooses. What it chooses is to arbitrarily fill the PrivateBooks
collection with a selection of Book objects.

This is convenient because it allows us to focus on data binding rather than Book getting.

Binding Your Business Object To The Listbox


Save all that, and return to the project in Blend where you will be told that your project has
changed (well, yes, it has, actually) as shown in Figure 7-12. Say yes, you do want to reload it.

(Well, you have to click the Yes button, actually saying "yes" out loud won't do a bit of good on
most computers, though you never know.)

Figure 7-12. Reloading Blend


It turns out that binding the Books collection to the ListBox is embarrassingly simple. Here's
how you do it.

Find the +Clr Object button on the Data tab (typically below the Project/Properties/Resources
panels. Click on it as shown in Figure 7-13

Figure 7-13. Clicking on CLR Object in the Data Tab

This opens the "Add CLR Object Data Source window" which will display all the potential data
source objects in your project. From here you can elect Library as your data source, as shown in
Figure 7-14
Figure 7-14. Binding the Library Class as the Data Source

Once Library is chosen, that result is reflected back in the Data window, and turning the arrow
next to Library reveals the properties, including its Property Books as shown in Figure 7-14 Drag
that Books object onto the Listbox and let go!
Figure 7-15. Books object in Data Window

When you let go, you are offered the opportunity to bind the Books collection to the list box as
shown in Figure 7-16

Figure 7-16. Binding Books Collection to ListBox

As soon as you choose to bind the Books collection the ListBox you'll be prompted as to which
field should be bound. With a list box the source list is bound to the ItemsSource, which is the
default field displayed, as shown in Figure 7-17

Figure 7-17. Create Data Binding


The instant you accept ItemsSource the grid is populated with one row for each book in the
collection, though not quite as you might hope. Run the application, let's see that bound data but
don't be too upset; we haven't told the list box quite enough yet for it to show you what you want.

Figure 7-18. Showing The Books - Almost

The problem the list box is having in Figure 7-17 is that you are asking it to bind to a very
complex object (a Book) which has a lot of properties. It has no idea what you want it to display,
and so in desperation it just shows you the type of the object.

All it needs is for you to provide it a template for what each line should look like, and then it will
happily zip though the collection applying your item template to each item in the collection.

You can write that item template by hand or in Blend.

Writing a ListItemTemplate By Hand

It turns out to be pretty easy to write a simple list item template by hand, so let's do that first.

Open the project in Visual studio and find the List box in Page.xaml. You'll find that it has a self-
closing tag. Un-self-close it!

<ListBox x:Name="BookListBox"
Margin="50,25,50,25"
Grid.Row="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding Mode=OneWay, Path=Books,
Source={StaticResource LibraryDS}}">

</ListBox>
Between the opening tag and the closing tag we'll place the ListBox.ItemTemplate and within
that a DataTemplate.

<ListBox x:Name="BookListBox"
Margin="50,25,50,25"
Grid.Row="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding Mode=OneWay, Path=Books,
Source={StaticResource LibraryDS}}">
<ListBox.ItemTemplate>
<DataTemplate>

</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Now it is just a matter of filling the DataTemplate with whatever controls you want to use to
bind to the properties in each book. Typically you begin with some sort of layout control. My
choice here is to use a stack panel and 5 text blocks to keep things simple. I'll use the first, third
and fifth to bind to properties of each book, and the remaining two as "prompts" for the data...

<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="DisplayListData"
Orientation="Horizontal"
VerticalAlignment="Bottom"
Margin="5" >
<TextBlock x:Name="Title"
Text="{Binding Title}"
Margin="5,0,0,0"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
FontFamily="Comic Sans MS"
FontSize="18" />

<TextBlock x:Name="ISBNPrompt"
Text="(ISBN: "
Margin="10,0,0,0"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
FontFamily="Verdana"
FontSize="14" />

<TextBlock x:Name="ISBN"
Text="{Binding ISBN10}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
FontFamily="Verdana"
FontSize="14" />

<TextBlock x:Name="CoverPrompt"
Text=") Cover Price: $"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
FontFamily="Verdana"
FontSize="14" />

<TextBlock x:Name="Cover"
Text="{Binding CoverPrice}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
FontFamily="Verdana"
FontSize="14" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>

Within the DataTemplate is a very straightforward StackPanel and within that are five nearly
identical TextBlocks. The first has its font size set to 16 and to Comic Sans MS, all the rest are
set to Verdana 14. The first, third and fifth are bound to the Title, ISBN10 and CoverPrice
properties of the book, respectively. When run, the list box now looks like Figure 7-19

Figure 7-19. Running with the ListItemTemplate

Creating the List Item Template in Blend

Before we proceed, let's rip out the list item template and recreate it in Blend. We just pull out
the entire template and make the list box self-closing again. We then rebuild the application and
then open it in Blend.

In Blend we click on the ListBox and then select Object → EditOtherTemplates →


EditItemTemplate → Create Empty as shown in Figure 7-20
Figure 7-20. Creating a ListItemTemplate in Blend

As soon as you make this choice, you are presented with the DataTmeplate Resource dialog box,
shown in Figure 7-21 where you can decide whether you want your data template to be an
application-wide resource or scoped to the current document (typically you'll choose the default
of putting the list item template in the document)
Figure 7-21. Create Data Template Dialog

Note, during beta there is a known bug, and if you open the lower drop down and choose list box
rather than the default “user Control” your code will not compile properly

Clicking OK creates an empty list item template which we can then populate like any other UI
element (in fact, Blend starts us off with a Grid control. We'll just add the stack panel to that as
shown in Figure 7-22. Once the stack panel is in place, we can then drag on the text blocks,
setting the binding just as we would with any other controls.

Figure 7-22. Creating the DataStackPanel for the ListItem

When creating the list box item template it will be important to bind the text blocks to the Books
collection as the data source, and to the properties of the Book class as the binding fields, as you
can see in Figure 7-23
Figure 7-23. Binding to Book Properties

Note that you create BookDS just like you created LibraryDS, by pressing on the +CLR object
button.

Implementing LibraryList_SelectionChanged
When the user clicks on a book, the LibraryList_SelectionChanged event handler will be called
and will take the following actions:

1. Resize the list box to make it smaller and to set it to span only one row
2. Set the details grid to be visible
3. Set a Book object (selectedBook) to the book the user chose
4. Set the dataContext for all the controls to be that selected book by setting the
DataContext of the DetailsGrid
5. Raise the PropertyChanged event from within the Book so that the UI is updated.

Creating the Second Grid


Return to Blend and hide the library list by clicking on the eyeball next to it in the Objects and
Timeline as shown in Figure 7-24

Figure 7-24. Hiding the LibraryList

Draw a Grid, DetailsGrid, into the third row of the existing grid, and use the stretch and margin
(=0) properties to have it fill the entire row.

Divide the DetailsGrid into six rows and two columns, and add the controls as shown in Figure
7-25
Figure 7-25. DetailsGrid divided into six rows and two columns

Binding the controls in the DetailsGrid grid.

There are eight controls in the six rows on the right side of the DetailsGrid, though in Figure 7-
24 six of them are not visible (they are TextBlocks with no text). The easiest way to describe
their position, attributes and binding is to show you the Xaml, though you will not create them in
Xaml, but rather by dragging TextBlocks onto the Artboard and setting their properties.

Please assume:

VerticalAlignment=”Bottom”
HorizontalAlignment=”Right”
FontFamaily=”Verdana”
FontSize=”18”
FontWeight="Medium"

Unless the Xaml states otherwise.

<StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="0"


HorizontalAlignment="Right" >
<TextBlock x:Name="NumAuthorsPrompt"
Text="{Binding NumAuthors, Mode=OneWay}"
Margin="0,0,15,15"/>

<TextBlock x:Name="AuthorsPrompt" Text="Authors"


Margin="0,0,15,15"/>

</StackPanel>

<ListBox x:Name="AuthorsListBox"
Height="Auto" Width="Auto"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Grid.Column="1" Grid.Row="0" Margin="10,0,20,0"
ItemsSource="{Binding Authors, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock
FontSize="14"
Foreground="Black"
Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

<TextBlock x:Name="PublisherPrompt" Grid.Row="1" Grid.Column="0"


Text="Publisher" Margin="0,0,15,15"/>

<TextBlock x:Name="Publisher" Grid.Row="1" Grid.Column="1"


HorizontalAlignment="Left" Height="Auto" Width="Auto" Margin="10,0,15,15"
Text="{Binding Publisher}" />

<TextBlock x:Name="EditionPrompt" Grid.Row="2" Grid.Column="0"


Text="Edition" Margin="0,0,15,15"/>

<TextBlock x:Name="Edition" Grid.Row="2" Grid.Column="1"


HorizontalAlignment="Left" Height="Auto" Width="Auto" Margin="10,0,15,15"
Text="{Binding Edition}" />

<TextBlock x:Name="PrintingPrompt" Grid.Row="3" Grid.Column="0"


Text="Printing" Margin="0,0,15,15"/>

<TextBlock x:Name="Printing" Grid.Row="3" Grid.Column="1"


HorizontalAlignment="Left"
Height="Auto" Width="Auto" Margin="10,0,15,15"
Text="{Binding Printing}" />

<TextBlock x:Name="YearPrompt" Grid.Row="4" Grid.Column="0"


Text="Publication Year" Margin="0,0,15,15"/>
<TextBlock x:Name="Year" Grid.Row="4" Grid.Column="1"
HorizontalAlignment="Left" Height="Auto" Width="Auto"
Margin="10,0,15,15" Text="{Binding PubYear}" />

<TextBlock x:Name="RatingPrompt" Grid.Row="5" Grid.Column="0"


Text="Rating" Margin="0,0,15,15"/>

<StackPanel Grid.Row="5" Margin="0,0,0,15" Grid.Column="1"


Orientation="Horizontal" >

<Slider x:Name="RatingSlider" Width="150"


HorizontalAlignment="Left" Margin="5,0,5,0"
LargeChange="1.0" SmallChange="0.1"
Minimum="0" Maximum="5.0" ValueChanged="RatingSlider_ValueChanged"
Value="{Binding Rating, Mode=TwoWay }" />

<TextBlock x:Name="SliderValueDisplay" Margin="5,0,0,0"


HorizontalAlignment="Left" />

</StackPanel>
</Grid>

Hiding the DetailsGrid

You want this grid to be invisible when the outer-grid (LayoutControl) is first displayed, so
change the Visibility property (line 65) from Visibility="Visible" to Visibility="Collapsed" and,
for convenience, unhide the Library List in the Objects and Timeline tab.

Creating the Event Handlers

You need an event handler to switch from one view to the other, and you need an event handler
for changes to the slider (note the inline event "ValueChanged"

Resize the List Box, Span Just One Row

Setting the size is a matter of setting the margins properly. Margins, it turns out, are instances of
the class Thickness.

As for setting the ListBox to span only a single row, here we must use the extended property of
the Grid, and to do that we use the SetValue method, passing in the Grid's property and the value
we wish to set it to.

private void LibraryList_SelectionChanged(


object sender,
System.Windows.Controls.SelectionChangedEventArgs e)
{
LibraryList.Margin = new Thickness(50, 5, 20, 5);
LibraryList.SetValue(Grid.RowSpanProperty, 1);

If you wish to see if this is working before going further, find LayoutRoot (the top Grid) and add
ShowGridLines="True" - then run the program and click on a book. You should see the list
control shrink above the lower dashed line, as shown in Figure 7-26

Figure 7-26. ListBox resized into Row 1

Returning to the event handler, the Details Grid has a visibility property; Intellisense will help
you set it properly to the enumerated value

Visibility.Visible.

Set a Book object (selectedBook) to the book the user chose

The selected book will come into your method through the SelectionChangedEventArgs as the
first member of the AddedItems collection. That will be an object whose underlying type is
Book, and you can cast it accordingly.

Book selectedBook = e.AddedItems[0] as Book;

Set the dataContext for all the controls to be that selected book by setting the
DataContext of the DetailsGrid

All of the controls inside a container inherit that container's DataContext unless they override it.
Thus there is no reason to write,

Printing.Text = selectedBook.Printing.ToString();
PublicationYear.Text = selectedBook.PubYear.ToString();
Etc. Instead you can just bind the text of these objects as we have done, and now set the
DataContext of their container,

DetailsGrid.DataContext = selectedBook;

Run the program and watch the magic, shown in Figure 7-27

Figure 7-27. Run the program, see the details

How many authors?


Two quick features to point out. In the first row the prompt was created in a stack panel

<StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="0"


HorizontalAlignment="Right" VerticalAlignment="Bottom">

<TextBlock x:Name="NumAuthorsPrompt" Text="{Binding NumAuthors,


Mode=OneWay}"
VerticalAlignment="Bottom" HorizontalAlignment="Right" FontFamily="Verdana"
FontSize="18" FontWeight="Medium" Margin="0,0,15,15"/>

<TextBlock x:Name="AuthorsPrompt" Text="Authors"


VerticalAlignment="Bottom" HorizontalAlignment="Right" FontFamily="Verdana"
FontSize="18" FontWeight="Medium" Margin="0,0,15,15"/>
</StackPanel>

The first TextBlock is bound to the mysterious property NumAuthors. We know that all of these
bindings are to the Book object; does the Book actually have a property NumAuthors that tells
how many authors the book has?

A quick check of Book shows that indeed this computation has been encapsulated in the Book
class (where it belongs):

public string NumAuthors


{
get
{
return privateAuthors.Count.ToString();
}
}

Finally, the slider control is initialized with the rating from the book (note the binding) but
allows the user to set any value between 0 and 5 and that value is written back to the business
object. This is accomplished by making the binding two way,

<Slider x:Name="RatingSlider" Width="150" VerticalAlignment="Bottom"


HorizontalAlignment="Left" Margin="5,0,5,0" LargeChange="1.0"
SmallChange="0.1" Minimum="0" Maximum="5.0"
ValueChanged="RatingSlider_ValueChanged" Value="{Binding Rating,
Mode=TwoWay}" />

Because the slider has no intrinsic display of its value, we've added a textBlock that will display
the Slider's value

<StackPanel Grid.Row="5" Margin="0,0,0,15" Grid.Column="1"


Orientation="Horizontal" >

<Slider x:Name="RatingSlider" Width="150" VerticalAlignment="Bottom"


HorizontalAlignment="Left" Margin="5,0,5,0" LargeChange="1.0"
SmallChange="0.1" Minimum="0" Maximum="5.0"
ValueChanged="RatingSlider_ValueChanged" Value="{Binding Rating,
Mode=TwoWay }" />

<TextBlock x:Name="SliderValueDisplay" Margin="5,0,0,0"


VerticalAlignment="Bottom"
HorizontalAlignment="Left" FontFamily="Verdana" FontSize="18"
/>

</StackPanel>

Note that the TextBlock is not bound to the Slider's value. Instead it is updated as a result of the
implementation of the Slider's ValueChanged event, ensuring that it is continually updated when
the user moves the slider, a much more satisfying user experience,
private void RatingSlider_ValueChanged(
object sender,
System.Windows.RoutedPropertyChangedEventArgs e)
{
Slider s = sender as Slider;
SliderValueDisplay.Text = s.Value.ToString("N");
}

Briefly, we must cast the sender (which is of type object) to type Slider so that we can access the
Slider's value property, which we then render as a string by calling ToString, passing in the
standard formatting string "N" which forces the string to render with 2 decimal places so that we
don't end up with a book rating of 4.7383928

[1] This is an incredibly obscure pun. In 1957 Francis Crick and James D. Watson suggested the
first accurate model of the Deoxyribonucleic acid (DNA) structure. DNA (Distributed interNet
Application Architecture was a precursor to .NET, and the image shown above is the lambda
repressor transcription factor bound to a DNA target, which especially makes me chuckle.

You might also like