DataBinding & DataTemplates Using Expression Blend
Oct 14, 2008
Login to Rate
:( Error
0
0
Summary
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 (Click to view full-size image)
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 (Click to view full-size image)
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 (Click to view full-size image)
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 (Click to view full-size image)
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 (Click to view full-size image)
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 (Click to view full-size image)
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 (Click to view full-size image)
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:
- 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.
- 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.
- 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:
- Data binding: bind the members of a list box to the Books collection in a Library
- 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 (Click to view full-size image)
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 (Click to view full-size image)
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 (Click to view full-size image)
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 (Click to view full-size image)
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 (Click to view full-size image)
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 (Click to view full-size image)
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:
- Resize the list box to make it smaller and to set it to span only one row
- Set the details grid to be visible
- Set a Book object (selectedBook) to the book the user chose
- Set the dataContext for all the controls to be that selected book by setting the DataContext of the DetailsGrid
- 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 (Click to view full-size image)
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 (Click to view full-size image)
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 (Click to view full-size image)
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. 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.