Displaying a collection of items can be considered as one of the highlights of using Silverlight. Silverlight contains many controls whose purpose is to display a list of items ranging from simple listboxes to a datagrid, which can be used to display an Excel-like representation of data. One of the most important aspects of displaying a collection of items is templating. With templating, we can change the way our items will render. The items of a collection can be either bound to the control or added manually through XAML or code. We will discuss the concept of binding later in this book, so don't be discouraged if you don't understand what it means right now. It's time to start exploring the items controls of Silverlight, and so we start with the ItemsControl
control.
ItemsControl
is the base class for the ListBox, ComboBox
, and TabControl
controls. Other than the usual properties, you might expect to find in a regular control that the ItemsControl
class contains some items-specific properties as well, as shown in the following table:
Property |
Description |
---|---|
|
This is a collection of items that the control will render. |
|
This is the source of the items. Usually, it is used with binding in XAML. |
|
This is the panel used to display the items. It can be templated to represent different kinds of panels (such as |
|
This is the data template, which is used to render an item. |
The key concept you need to keep in mind when working with the ItemsControl
control (or any control that is derived from it) is that you have to template your items. As we go further in this subject, we will discuss this concept even further.
Before we discuss this topic further, create a new Silverlight application in Visual Studio 2010 and name it Chapter2-Collections.
One of the most commonly used controls to display a collection of items is the ListBox
control. Using this control, you can show a collection of items at the same time on the screen, exposing the selected item and enable scrolling if not enough room is available for the control to show all the items at once. The ListBox
control uses the Items
property to load its collection of items at runtime. An example of this can be seen in the following code snippet:
<ListBox x:Name="MyFirstListBox"> <ListBox.Items> <ListBoxItem Content="Item 1"/> <ListBoxItem Content="Item 2"/> <ListBoxItem Content="Item 3"/> <ListBoxItem Content="Item 4"/> <ListBoxItem Content="Item 5"/> </ListBox.Items> </ListBox>
When we run the preceding code snippet, we will get the result as follows:
As we can see from the preceding code snippet, each item of the ListBox
control is represented as a ListBoxItem
element. ListBoxItem
is a ContentControl
type of control and as such, doesn't have to be just text. We can use almost any control we like as the content of ListBoxItem
. The ListBox
control allows us to set a data template to style our items as well, but this is a more advanced topic, and we will discuss it in detail in the next chapter. In order to work with the items of ListBox
, the control exposes a number of properties and events. The SelectedIndex
and SelectedItem
properties allow you to either get the index number, or the object of the currently selected item, or set an item as the currently selected item by specifying its index or object. When using the SelectedIndex
property, it is important to remember that it is zero based and, as such, begins its count with 0. To know if the user has selected an item or changed his/her selection, we can use the SelectionChanged
event. Every time a user changes his/her selection, the SelectionChanged
event fires, letting you perform some business logic.
The ComboBox
control should be familiar to you from just about any other language you use. The ComboBox
control provides the user with the ability to select a single option from a list of predefined choices. These choices are visible to the user as long as ComboBox
is open. In most cases, you will need to give the user the ability to open or close ComboBox
, but if you want more control over this, you can use the IsDropDownOpen
property from code behind to either get or set the ComboBox
drop-down portion. An example of a simple ComboBox
can be seen in the following code snippet:
<ComboBox x:Name="MyFirstComboBox" Width="100" Height="30"> <ComboBox.Items> <ComboBoxItem Content="Item 1" IsSelected="True"/> <ComboBoxItem Content="Item 2"/> <ComboBoxItem Content="Item 3"/> <ComboBoxItem Content="Item 4"/> <ComboBoxItem Content="Item 5"/> </ComboBox.Items> </ComboBox>
The preceding code snippet will render ComboBox
with predefined dimensions, set a list of the ComboBoxItem
elements, and set the first item as the currently selected one. (Quick quiz: what would be the value of MyFirstComboBox.SelectedIndex
right now?)
The preceding ComboBox
would look as follows once rendered:
And it would look as follows when in an open state:
Just like the ListBox
control, each of the ComboBoxItem
controls is of the ContentControl
type, and as such, their content can be set to just about almost any Silverlight control.
Just like the ListBox
control, the ComboBox
control exposes the SelectedIndex
and SelectedItem
properties, but it also exposes the MaxDropDownHeight
property, which allows you to set the maximum possible height for the drop-down portion of ComboBox
once opened. If the maximum height is not enough to fit all the items, a vertical scroll bar will appear. In addition, the ComboBox
control exposes two events—DropDownOpened
and DropDownClosed
. These events fire when the user (or you, using the IsDropDownOpen
property) opens and closes the drop down.
The TreeView
control allows you to display hierarchical data in a tree structure. In addition to that, the TreeView
control has the ability to expand or collapse an item displayed in it. The TreeView
control inherits from ItemsControl
, which means that you can use the Items
or ItemsSource
properties to set its content. Using the TreeView
control is quite straightforward. It can be seen from the following code snippet:
<sdk:TreeView x:Name="MyFirstTreeView"> <sdk:TreeViewItem Header="Products"> <sdk:TreeViewItem Header="Books"/> <sdk:TreeViewItem Header="Movies"/> <sdk:TreeViewItem Header="Books"/> </sdk:TreeViewItem> <sdk:TreeViewItem Header="NoItems"/> <sdk:TreeViewItem Header="Rectangles" IsExpanded="True"> <Rectangle Width="10" Height="10" Fill="Beige"/> <Rectangle Width="10" Height="10" Fill="Azure"/> <Rectangle Width="10" Height="10" Fill="Bisque"/> </sdk:TreeViewItem> </sdk:TreeView>
The preceding code will render as follows:
As can be seen from the preceding code, the TreeView
control lies in the sdk
namespace. The easiest way to add a TreeView
control to your page is to simply drag it off from Visual Studio or Blend's toolbox. By doing that, Visual Studio or Blend will add the required references and namespaces to your project. The scheme for using the TreeView
control is quite simple. You have a parent TreeView
control, which hosts a bunch of the TreeViewItem
elements. Each TreeViewItem
element can also nest the TreeViewItem
elements inside of it. As TreeViewItem
is also of the ContentControl
type, you can use just about anything you like as its content, just like the last TreeViewItem
control in our example, which nests three rectangles. TreeViewItem
has two important properties as follows:
Header:
This is the text to display as the header for the nodeIsExpanded:
This is a Boolean property, which determines whether the node should be opened or closed when the application first runsThe header of your TreeViewItem
isn't limited to being just text. As the header is also of the ContentControl
type, you can very easily display other controls as the header for your TreeViewItem
. By changing the NoItems
node to the following code snippet, we can also display an image next to the text:
<sdk:TreeViewItem> <sdk:TreeViewItem.Header> <StackPanel Orientation="Horizontal"> <Image Source="Images/xicon.png"/> <TextBlock Text="No Items!"/> </StackPanel> </sdk:TreeViewItem.Header> </sdk:TreeViewItem>
The NoItems
node will now render as follows:
The DataGrid
control is used to show information in a tabular form. One of the coolest features of the DataGrid
control is its ability to allow users to add, edit, delete, select, and sort items that are bounded to it. The DataGrid
control uses its ItemSource
property to bind data for display. Another important aspect of the DataGrid
control is its usage of a feature known as UI virtualization. UI virtualization ensures that while the DataGrid
control handles all its items in memory, only the required UI elements needed to display the items are actually created. Imagine the performance gained from this feature when you have a collection of a million items. If DataGrid
didn't use this feature, it would load all the million items to the memory and create all the required UI elements for all the million items, but with UI virtualization, even though DataGrid
holds the million items in memory, only the data that the user can actually see is displayed on the screen. The ListBox
control we discussed earlier also supports this feature. As we haven't discussed the concept of data binding yet, it may look odd to you. Don't worry if you don't get it right away; it will be greatly explained in Chapter 5, Working with Data. For now, just follow the code and try to understand the DataGrid
control's properties and schemes.
The easiest way to get a DataGrid
control to display data is to set its AutoGenerateColumns
property to True
. Doing that will tell DataGrid
that you are not interested in templating the data you wish to display and you want everything to be done automatically. As a basic example, let's assume we have a list of objects named Food
, which represents foods and contains two properties—the name of the food and a link to the image that represents it. Add a DataGrid
to your page by dragging it out of the toolbox, and set its properties, as shown in the following line of code:
<sdk:DataGrid x:Name="MyFirstGrid" AutoGenerateColumns="True" />
Now switch to the code behind file (MainPage.xaml.cs
), and add the following line of code inside the constructor method:
List<Food> foods = GetFoods(); MyFirstGrid.ItemsSource = foods;
Run the application, and you should get the following result:
We've bound a list of objects to our DataGrid
control, and because we told it to autogenerate the columns itself, we've got a nice textual representation of our object!
If you try to double-click on any of the DataGrid
control's cells, you'll notice you can edit the text. Click on one of the headers and the DataGrid
control will sort the data in ascending or descending order. You can react to these types of events using the CellEditEnded
and CellEditEnding
events. The first one fires after the edit has ended, and the second one fires just before the edit will end.
You can also implement your own logic for adding new items or deleting old ones.
Watching that sample, you will notice that DataGrid
renders our object with text. This happens because the default column type that DataGrid
uses when set to automatically generate the columns is DataGridTextColumn
. This type of column is used to display text, and because of that, the column will call the bound object's ToString
method in order to display it. The one exception to this rule is Boolean values. When the DataGrid
control tries to render a Boolean property, it will use DataGridCheckBoxColumn
. This type of column will render a CheckBox
control to represent the true/false values.
When we want to have more control over the rendered content of DataGrid
, we set the AutoGenerateColumns
property to False
and define our own template for the cell. As we haven't discussed templating yet, this feature will not be explained in much detail here. To control how a cell renders its content, we can use the DataGridTemplateColumn
element. This type of column allows us to set a data template (a template for the data we wish to display) for the bound data. Change your datagrid according to the following code snippet:
<sdk:DataGrid x:Name="MyFirstGrid" AutoGenerateColumns="False"> <sdk:DataGrid.Columns> <sdk:DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="Auto"/> <sdk:DataGridTemplateColumn Header="Image"> <sdk:DataGridTemplateColumn.CellTemplate> <DataTemplate> <Image Source="{Binding Image}"/> </DataTemplate> </sdk:DataGridTemplateColumn.CellTemplate> </sdk:DataGridTemplateColumn> </sdk:DataGrid.Columns> </sdk:DataGrid>
The preceding code will render as follows:
Once we set the AutoGenerateColumns
property to False
, we have to define each and every column we wish to display manually. For the Name column, we've used the DataGridTextColumn
type of column as we only wish to display text. We bind that column using the Binding
property to the Name
property of the Food
class. For the Image column, we've used the DataGridTemplateColumn
type of column as we need to define a template for that column, so instead of text it will render an image with the source bound to the Image
property of the Food
class.
As mentioned earlier, the cells of the DataGrid
control can be edited by the user. If you wish to prevent such editing, set the IsReadOnly
property of DataGrid
to False
. If you do use editing in your DataGrid
, you have a set of events aimed at helping you control the editing process. The BeginningEdit
event fires just as soon as the cell enters the editing mode. Using this event, you can do things, such as preventing a user from editing the cell. The event exposes a Boolean property named Cancel
within its EventArgs
, which, when set to True
, stops the event from running. If the BeginningEdit
event successfully completes, the PreparingCellForEdit
event fires next. This event gives you the ability to override any changes made in the BeginningEdit
event. When these two events finish running successfully, the previously mentioned CellEditEnding
and CellEditEnded
events fire.
The last concept of DataGrid
, which we will discuss is sorting. DataGrid
has built-in support for sorting collections of items that implement the IList
interface. This interface is used almost everywhere in Silverlight, due to the fact that it allows DataGrid
to sort almost any collection of items immediately. Each DataGrid
column type has a property named SortMemberPath
, which allows you to set the name of the collection that this column will be sorted with. Take our Image column for example. If you click on it right now, it won't sort anything, because the column doesn't know how to sort images, but if you click on the Name column, the grid will sort itself by that column. To give the Image column the same ability, set the SortMemberPath
property of the image's DataGridTemplateColumn
to any of the property's name of the Food
class. In our example, it doesn't matter which property you set as the names of the images and foods is the same.
As you can see, DataGrid
is a powerful control, which can be of great help when you want to create applications that let a user view and edit data. It is strongly encouraged that you play with the control a bit more to fully grasp all its power. As for binding, don't worry if you didn't understand the concept fully. We will dig much deeper into the subject when we reach Chapter 5,Working with Data.
3.133.107.25