0% found this document useful (0 votes)
27 views

DataTemplates Tutorial 07 CS

Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
27 views

DataTemplates Tutorial 07 CS

Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 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-59651039-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-59651845-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, "0596-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 selfclosing 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. 2. 3. 4. 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 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 724 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" 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> Text="Authors"

<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" Orientation="Horizontal" > Grid.Column="1"

<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