The Silverlight ListBox is much more than you may think. When most people think of a ListBox they typically think of just a simple control that displays rows of text. However, the Silverlight ListBox is one of the most flexible controls you will find. I tend to think of the ListBox as similar to the ASP.NET Repeater control as it too is very flexible. In this article, I will show you six different ways to display data in a Silverlight ListBox.
For all the tips in this article I will use a simple Product class (Listing 1) with seven properties; ProductId, ProductName, ProductType, Price, Image and IsOnSpecial. This class holds the information about a specific product such as its product type, its price, an image for the product and whether or not that product is on special.
As you could guess, you will most likely have more than one product to display in your ListBox. So, you will need to create a Product collection class (Listing 2) to hold many Product objects. In this article you will see how to create a mock set of product objects and fill in a property called DataCollection in the Products class with this mock set of products.
Create an Instance of Products in Your XAML
Now that you have some data in your Products class, you need to create an instance of this class so you can display that data in a ListBox. You do not need to write code to create an instance of this class; you can have your XAML create the instance. You do this by first adding a namespace to your <UserControl> element that references the assembly in which your Products class is located.
xmlns:data="clr-namespace: Silverlight_ListBox"
Next, you add a <UserControl.Resources> section and create the instance of the products class from the “Silverlight_ListBox” namespace referenced by the local XAML namespace that you called “data”.
<UserControl.Resources>
<data:Products x:Key="productCollection" />
</UserControl.Resources>
The XAML above creates an instance of the Products class and assigns it the key name of “productCollection”. To use this resource as the source of data to a ListBox, you simply set the ItemsSource as shown in the snippet below:
<ListBox x:Name="lstData"
ItemsSource="{Binding
Source={StaticResource productCollection},
Path=DataCollection}">
By binding to the static resource productCollection you can access the property called DataCollection which you remember holds the collection of our mock product objects. Ok, now that you know what the sample data looks like and how to bind it to a ListBox, let’s now take a look at various methods of displaying data using the ListBox control in Silverlight.
Tip 1: Display More Than One Column
A XAML ListBox can display two (or more) columns just as easily as one column. In the example shown in Figure 1 you see two columns; one for the product name and one for the product price. This type of ListBox is very easy to create with just a little bit of XAML code shown in Listing 3.
As described previously, you have already created a resources element that references the products collection of your user control. Now you can create the template that will be used to display each product in the ListBox control. This is done by adding the ListBox.ItemTemplate element to the XAML for the ListBox control, and then you add a DataTemplate element nested within the ListBox control ItemTemplate.
The DataTemplate is the most important element in a ListBox as it tells Silverlight how to present each row of data. This template starts with a StackPanel that “stacks” child elements horizontally by using the Orientation=”Horizontal” attribute. The StackPanel contains two child elements, a TextBlock for the product name and another TextBlock for the product price. Note that the TextBlock elements define the width of the “columns” for the ListBox by using the Width attribute.
The DataTemplate is the most important element in a ListBox as it tells Silverlight how to present each row of data.
Each TextBlock control is bound to a corresponding property of the Product using XAML data-binding notation. The first TextBlock has its “Text” property set to “{Binding Path=ProductName}”. This notation tells XAML to get the value for the ProductName property for the current row of data being displayed and places that value into the Text property of this TextBlock.
The same binding notation is used for the Price column, setting the Path to the Price property of the Product object. In addition, the binding notation allows you to specify a .NET format string for value types. In this case the “c” format is used to take the decimal price value and format it as a currency.
When the user control is loaded, Silverlight creates an instance of the Products class, the DataCollection property is initialized and Silverlight binds the items in the collection and the result is displayed as shown in Figure 1.
Tip 2: Spice Up the ListBox with Images
Since the main example uses a graphical user interface, i.e., a browser, it makes sense that you want to display images in addition to just text. After all, a picture is worth a thousand words as the old saying goes. Using images next to product information can make it a little clearer about the actual product you are purchasing. In this next sample (Listing 2) you will see how to use a ListBox to display images in addition to textual data.
In this example, you will see how to use a Silverlight ListBox control to display product images and product information in multiple columns, one column for the product image and two columns for the product name and price.
Again, you’ll use a DataTemplate to define the look and feel for each row of the ListBox. Listing 4 shows the XAML code for the ListBox illustrated in Figure 2.
The DataTemplate in Figure 2 is almost identical to the ListBox described in Tip 1. The only difference is the addition of an Image control prior to the two TextBlock controls. The Image control adds both Width and Height attributes to describe the total area that the image will take up in this row of data. The Source property of an Image control will be a valid URI that points to where the actual image is located in relationship to the Silverlight control. Assuming that the images are located in an Images folder than the “Image” property of the Product class would hold a path such as “Images/Framework.jpg”.
Tip 3: Split Data Across Multiple Lines
Since you are allowed to use almost any XAML within the DataTemplate for a ListBox, you are not limited to just a single stack panel and text block controls. Think of each row of the ListBox as being just another user control into which you can place other XAML controls. If you look at Figure 3 you can see that you now have a border around each row of data. You also have the image on the left and you have the name of the Product above the price of the product. Listing 5 shows the XAML code for the ListBox shown in Figure 3.
Let’s now break down each piece of XAML contained within the DataTemplate of this ListBox. The first piece is the Border control that wraps up each row of data. Next is a StackPanel that stacks the elements of the row horizontally due to the Orientation attribute being set to Horizontal. You now list the items that you want to stack horizontally; they are the Image control and another StackPanel. The second stack panel is a normal vertical StackPanel, so inside of this is where you place the TextBlock control that displays the product name and another TextBlock control that displays the price below the product name.
Tip 4: Display Your Data Horizontally
Let’s now take the ListBox we just looked at and turn it on its side. That’s right, a ListBox in XAML does not just have to display data vertically - you can change the orientation of the ListBox so it will display the data horizontally (see Figure 4).
To make a ListBox display its data horizontally you simply need to add a <ListBox.ItemsPanel> element to the ListBox Listing 6). In the <ItemsPanelTemplate> element you just need to specify that you wish to use a StackPanel with its Orientation attribute set to “Horizontal”. Many XAML controls have an ItemsPanel template. This template controls the orientation and layout for the control as a whole, not the individual items within the control. So, in this case, the ItemsPanel is telling the whole ListBox to display horizontally. Each row in the ListBox is still laid out according to its DataTemplate.
The ItemsPanel element controls the overall look of a ListBox and thus can be used to display a ListBox horizontally.
Tip 5: Change the Look and Feel at Runtime
Silverlight has the flexibility to modify the look of your controls dynamically at runtime with just a few lines of code. Take a look at Figure 5 and Figure 6 and you will see two different views of the same ListBox and data. To accomplish this, you simply need to set up two different DataTemplate sections as resources. With just a couple of lines of code you can then switch between two DataTemplate resources at runtime.
To create the Silverlight page shown in Figure 5 you will create a Grid with two rows. The first row is where you will place the More and Less buttons. The second row is where you place the ListBox. Listing 7 is the XAML used for the <Grid> portion of this page with the buttons and the ListBox shown. Notice in the ListBox that instead of specifying an ItemTemplate area you instead reference a StaticResource. You will learn about this StaticResource in a minute.
After you have created the Grid, the Button controls and the ListBox control, you are now ready to create the DataTemplates that will be used to fill in the ItemTemplate area of the ListBox. The first thing you need to do is to create a <UserControl.Resources> section in your User Control. You will create two data templates as keyed resources in this resources section. One DataTemplate has a key set to “tmplMore” and one has a key set to “tmplLess”. Listing 8 is the DataTemplate that is used for the more detail view of your ListBox (Figure 5).
You now create a DataTemplate resource with a key set to “tmplLess”. This template is used to create the ListBox with less detail (Figure 6). The XAML for this DataTemplate is shown in the code snippet below.
<DataTemplate x:Key="tmplLess">
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="16"
Text="{Binding Path=ProductName}" />
</StackPanel>
</DataTemplate>
In Listing 7 you pre-assigned the DataTemplate with the key of “tmplMore” to the ListBox. This is just so you have something to look at in design mode. Now let me show you how to dynamically switch between the two keyed DataTemplate resources at runtime.
The Click event procedure of each button is set to call the same method. This method name is called ChangeTemplate. Notice the use of the Tag property in each button. The Tag property is set to the key name of the DataTemplate you created in the UserControl.Resources section.
<Button Name="btnMore"
Tag="tmplMore"
Content="More"
Click="ChangeTemplate" />
<Button Name="btnLess"
Tag="tmplLess"
Content="Less"
Click="ChangeTemplate" />
Listing 9 shows the ChangeTemplate() event procedure called by each of the button’s Click event.
While you could have written the above procedure as a single line of code, I broke it out so you can more easily read and understand the code. First you grab the Tag property of the button which raised this event. Store this value into the “templ” variable. Next you use the Resources property of the UserControl class to locate the resource with the name you pass to this indexed property. You will need to cast the resource you get back as a DataTemplate object. You then assign this DataTemplate object to the ItemTemplate property of the ListBox.
Tip 6: Display Conditional Text Using a Value Converter Class
When you bind data to your Silverlight controls you will probably have data that is in one format that needs to be converted to another format. For example, if you have a decimal number (price) that is 19.95, you would want to display it as $19.95. Of course, you can use a normal .NET format string to accomplish this, but you could also write some code to convert the decimal number 19.95 to a string with the dollar sign in front of it “$19.95”.
In this sample you will use the IsOnSpecial property of the Product class to determine whether or not to display the text “** ON SPECIAL **” for each row of data (see Figure 7). The IsOnSpecial property is a Boolean value so you need to convert that value from a True/False value to a string.
To accomplish this you need to tell Silverlight that instead of displaying the IsOnSpecial property it should pass the value to a method in a class you create and have that method return a string back to be displayed.
The OnSpecialConverter Value Converter Class
As mentioned at the beginning of this article, you need to create a class with a method to convert a Boolean value to a Bitmap image. This class must implement a special interface called IValueConverter. This interface only has two methods; Convert() and ConvertBack(). Most times you will only implement the Convert() method. Listing 10 is the class called “OnSpecialConverter” that implements the two methods Convert and ConvertBack.
In the Convert method the main parameter you will be working with is the “value” parameter. This is the value that was bound to in the XAML. In this example that is the IsOnSpecial value from the Product class. So, the value “True” or “False” is passed into the Convert method in the “value” parameter. Your job is to check this value parameter and if the value is “true” return the string “** ON SPECIAL **” otherwise return an empty string.
Namespaces and Resources
Now that you have this class created you will have your Silverlight user control create an instance of that class. To do this, you first add the namespace where this class was created. At the top of your user control you add an xmlns tag as shown below.
xmlns:local="clr-namespace:SLValueConverter"
The “local” is any name you want to create. Visual Studio will then display a list of namespaces that are available in your Silverlight project. In the example above, the name of the project is “SLValueConverter”.
Next, you create a UserControl.Resources section in your XAML where you can create an instance of both the Products collection class and the value converter class.
<UserControl.Resources>
<local:Products x:Key="productCollection" />
<local:OnSpecialConverter
x:Key="specialConvert" />
</UserControl.Resources>
The code above creates an instance of each class when the Silverlight user control is created. These two classes can now be used anywhere in this user control.
The ListBox with Value Converter
To create the ListBox shown in Figure 7, you will use the same XAML that was shown in Tip 3. You will just add one more TextBlock control after the text block that displays the price. This new TextBlock is defined as shown in the code snippet below.
<TextBlock Margin="18"
FontSize="18"
Text="{Binding Path=IsOnSpecial,
Converter={StaticResource
specialConvert}}" />
The Binding syntax is the same as you have seen as far as binding to the property of the class to which this ListBox is bound. The difference is you now specify a Converter after the path. The Converter is bound to a resource in your project. In this case it is bound to the resource you created in the UserControl.Resources section that created an instance of the OnSpecialConverter class. When each row in the ListBox is evaluated, the IsOnSpecial value is passed to the “value” property of the Convert method of the OnSpecialConverter class. Then either some text is returned, or not.
Summary
The Silverlight ListBox control has a tremendous amount of flexibility in how it can display data. In this article, you used one set of Product data and you learned how to display that data in a myriad of ways. There are many more things you can make the ListBox do as well. The most important take away from this article is that XAML is very powerful and learning just a few basics opens up all sorts of great UI possibilities.
NOTE: You can download the complete sample code (in both VB and C#) at my website. http://www.pdsa.com/downloads. Choose Tips & Tricks, then “Six Silverlight ListBox Tips” from the drop-down.