Displaying a collection of items

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

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

Items

This is a collection of items that the control will render.

ItemsSource

This is the source of the items. Usually, it is used with binding in XAML.

ItemsPanel

This is the panel used to display the items. It can be templated to represent different kinds of panels (such as Grid, StackPanel, and so on.)

ItemTemplate

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.

ListBox

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:

ListBox

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.

ComboBox

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:

ComboBox

And it would look as follows when in an open state:

ComboBox

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.

TreeView

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:

TreeView

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 node
  • IsExpanded: This is a Boolean property, which determines whether the node should be opened or closed when the application first runs

Note

The header can be more than just text!

The 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:

TreeView

DataGrid

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.

Displaying data in DataGrid

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;

Note

The GetFoods method returns a list of the Food objects to the local foods list. The code for this method can be found in the book code pack that you can download from the Packt website under the Chapter2-Collections project.

Run the application, and you should get the following result:

Displaying data in DataGrid

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.

DataGrid templating

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:

DataGrid templating

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.

Sorting

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.105.159