Adding Controls, Data, and Behavior
Now that you have the basic layout in place, you can begin adding controls. This example uses ListBoxes to show the list of teams and player roster for a team. In standard Windows Forms applications, developers use ListBox controls to display simple lists, most of which are short and ugly. In WPF/Silverlight on the other hand, developers use ListBoxes as workhorse objects to display large amounts of data in very flexible ways.
Add Listbox controls to both the first and third columns. Make sure you set their width and height to Auto
and their vertical and horizontal alignments to stretch
. Also, set the margins of the outside edges to 8
pixels, and the margins for their inside edges (near the GridSplitter) to 2.5
pixels (because the grid splitter automatically adds some "whitespace" between the two list boxes). This creates a nice overall appearance, no matter what size the main window is, and no matter how the user chooses to size both "panels."
|Figure 4. Declarative Data Binding: In some cases, binding to CLR objects can be completely declarative and may not require any coding.|
Now you need to bind the two lists to data. Given the right preconditions, WPF/SL can bind to data in a downright simple way. For instance, you can bind controls to an XML file or a .NET object that acts as the data source without writing any code. However, such objects must be creatable without any constructor parameters or any method calls of any kind. In other words, you couldn't call a business object that returns a DataSet. Instead, a simple New
operation must be sufficient.
In the real world, you are not very likely to use XML files as data sources for sophisticated applications (although XML served up by a web service is a more likely scenario). Most applications use objects of some sort. Most real-world scenarios require involved method calls or instantiation parameters to indicate what data you want to load. However, the example for this article happens to be a team data object that you can instantiate without any parameters. On creation, that object loads all the teams from the database and exposes them as a collection object. That's convenient for example purposes, but it is somewhat unusual. You'll see how to call more common data sources when you see how to populate the Players list.
To bind the Teams list to data, right-click it and select "Bind ItemsSource to Data
" from the context menu. This launches the Create Data Binding dialog box shown in Figure 4
. Click the "+ CLR Object" button to get a list of all the CLR objects available in the current project that may be used as data sources.
|Author's Note: Make sure you have added a reference to your middle-tier assembly in your project.
|Figure 5. Basic Data Template: Define a very basic data template for each item in the teams-list.|
Close the dialogs and run the application (press F5 either from Blend or Visual Studio after saving all files) to see the data binding in action. At this point, the list does not look very nice, because it's simply bound to a list of TeamBusinessEntity objects, without specifying what fields from within those objects are to be displayed. Therefore, the list displays only the default ToString()
result for each row, which is simply the name of the object. To fix this problem and display the specific property (or multiple property) values, you need to define a data template.
Right-click on the Teams list and select the Create Data Binding option. This brings up a dialog box (see Figure 4
). Click the "Define Data Template" button, which launches the Create Data Template dialog box shown in Figure 5
. To define a data template, pick one or more data source members to display inside each list item. In WPF/SL, list boxes can contain complex items. In fact, each list item can be a Grid object (which is not the default and thus has to be picked explicitly), which—as you saw above—is its own microcosm that can include just about any other object.
Using a Grid as the container object will ultimately allow you to create an elaborate design for each list item. Initially, however, just put a text block inside the grid that is bound to the team name. Finally, check the "Generate sample data" checkbox. This will create nonsensical sample data during design time (names of fruits), that is nevertheless convenient to have, because it forces the list box to show items in the designer, enabling you to see the impact of settings and changes immediately, without having to run the application.
You can now run the application and see the data binding in action, including the correct display of team names.
What is missing at this point is the display of the players when a team is selected. As noted above, this is a little trickier than the display of teams because you have to make a call to a middle-tier object to retrieve an appropriate list of players from a potentially very large list of players stored in the database. So you can't just pick an object in a Blend dialog. Instead, you have to write an event triggered when a user selects a new item. The event code must retrieve the appropriate data and assign that to the second list box as its data source. The following code snippet shows an event handler that accomplishes just that:
private void lstTeams_SelectionChanged(
object sender, SelectionChangedEventArgs e)
if (this.lstPlayers != null)
var teams = this.lstTeams.ItemsSource
Guid teamId = teams[
PlayerList players =
this.lstPlayers.ItemsSource = players;
Wire up this code to the SelectionChanged
event by double-clicking that event in the event list in Blend (the event list is the secondary mode of the property grid), which automatically switches to Visual Studio (or launches it if you haven't started it already) and creates the empty event handler code.
The code itself makes sure the player list has been initialized (because it's possible this code could get called during initialization of the team list—before the player list has ever been created). Then, it retrieves a reference to the data source, which retrieves data using the primary key (PK) of the currently-selected team. It returns a PlayerList business entity, which is a middle-tier object that represents a list of players.
|Author's Note: Your implementation of the middle tier may vary drastically, but the overall idea of writing some code that ultimately returns appropriate data remains the same.
The returned data is then assigned as the data source for the Players list. (Note that I used Blend's property grid to assign names to both list boxes.)
If you run the application now, you will see that everything works, sort of—but the Players list has the same flaw that originally happened with the first list; instead of player names, it displays only the object name for each bound object. The root problem is the same as before: You need a data template that defines which data elements to show in each list item. You'll have to fix this problem using a different technique than before, however, because you cannot use Blend's data binding dialog box. One approach is to create a new data template from scratch by right-clicking on the second list box and picking the menu option Edit Other Templates → Edit Generated Items (ItemTemplate) → Create Empty. However, there's a simpler trick. Because you already have a similar template in the first list box, you can simply apply that template, save it as a new template, and then modify it to bind the text block to the appropriate property. To do this, right-click the list box and select Edit Other Templates → Edit Generated Items (ItemTemplate) → Apply Resource, and then pick the existing template you created earlier.
|Figure 6. Editing Bound Properties: Bound properties appear outlined in yellow in the property sheet.|
At this point, both lists use the same data template, which is now shared between them. That's not exactly what you need, because you want the second list to bind to a differently named property. While each team name is simply stored in a Name
property on the data object, player names are stored in a SearchName
property. So you need to change the binding expression—but not in the shared template: that would break the first
list. Instead, save a copy of the assigned template, giving the second list its own version of the data template, which you can then modify without breaking the first one. To do that, right-click the control again and from the menus choose Edit Other Templates → Edit Generated Items (ItemTemplate) → Edit a Copy
. Choose a name for the new template. Blend then automatically switches into visual template-editing mode. If you're following along, you can click on the text block and observe how the Text property has a yellow outline in the property sheet (see Figure 6
). Click the little yellow square to the right of the property value to bring up the context menu and pick "Data Binding
". In the dialog box, change the "custom binding expression" to SearchName
, and click Close. Run the application again to see it in its fully functional form.