Silverlight ListBox: Part I - Using multiple templates in a ListBox
Introduction
The listbox is a very important control in any technology like ASP.NET or Sliverlight. Normally, we use it to show single line records where the user has the option to choose one of them. I am not going to discuss about how to use listbox in Silverlight as there are many articles available on the topic in the Internet. In this article, I will discuss about how we can use a template to show the items in a listbox. Although there are many articles available on using a single template in a listbox, there are very few which discuss about using multiple templates in a listbox. Sometimes we might have a requirement where we want to show items which have a few common properties and a few specific properties, and we might want to show a different UI for each type of item and in the same listbox.
Let’s take an example to get a better understanding.
It is a very common scenario. There is an organization which has software developers, team leaders, and managers. All of them have a common properties like EmployeeName and ManagerName, but team leaders and managers will have direct reporters to them.
I am going to create a list box which will have software developers, team leaders, and mangers in a list box. Each will have common properties like Employee Name and Manager Name. Team leader and manager items will have listbox which will have a list of reporters. Following is the list of background colors that I used for each type of item to distinguish them.
Item Type | Background Color |
---|---|
Software Developer | Yellow |
Team Leader | Cyan |
Manager | Green |
Background
There is a very good sample on listbox which uses a Panel
as a container of the listbox. Normally, we use a listbox with the listbox items arranged vertically even if there is enough horizontal space. This sample implements a wrap panel for the listbox; if you are using a wrap panel, then you need not worry about the list box item positions. If there is enough horizontal space, then multiple items can rendered in the same line, and if there is not enough space for the next item, then the next item would be rendered in the next line. Apart from it, its also takes care of resizing the listbox so if the end user is resizing the listbox, then the items would be rendered again and will take new positions corresponding to the space available. This sample also describes about an animation but I am not going to use that in my sample. Following is the link for the article:
You can directly follow up my example even without reading the above article, but I would highly recommend reading it.
What is the Idea?
Following are the steps that we will follow to achieve our goal. The steps would be more clear in the code section.
- Create a template class which will have properties for all the possible templates in the listbox.
- Create a new class which will derive from
ListBox
. It will have a new dependency property to select the template for a givenListBoxItem
. Override thePrepareContainerForItemOverride
method as it actually prepares the specified element to display the givenListBoxItem
. It should call the base class (ListBox
)PrepareContainerForItemOverride
method and set the template of the item from theTemplateSelecter
property. - Define the templates for each type of item in the XAML resource.
- Define a
TemplateSelecter
in the XAML resource which will refer to the data template for the item types in theListBox
. - Refer this
TemplateSelector
in the list box declaration in the User Control XAML.
Using the Code
Step1
Define the classes for each type of item. All the classes should have a common base class. Here, Employee
is the common base class.
public class Employee
{
public string Name { get; set; }
public string ImageSource { get; set; }
}
public class Developer : Employee
{
public string ManagerName { get; set; }
}
public class TeamLeader : Employee
{
public List<Employee> DirectReports { get; set; }
public string ManagerName { get; set; }
}
public class Manager : Employee
{
public List<Employee> DirectReports { get; set; }
}
Step 2
Create a EmployeeTemplateSelecter
class which should have all the used templates in the ListBox
and should have a SelectTemplate
method which would return the template.
public class EmployeeTemplateSelector : DataTemplateSelector
{
public DataTemplate EmployeeTemplate { get; set; }
public DataTemplate DeveloperTemplate { get; set; }
public DataTemplate TeamLeaderTemplate { get; set; }
public DataTemplate ManagerTemplate { get; set; }
public override DataTemplate SelectTemplate(object item,
DependencyObject container)
{
if (item != null)
{
if (item is Manager)
{
return this.ManagerTemplate;
}
if (item is TeamLeader)
{
return this.TeamLeaderTemplate;
}
if (item is Developer)
{
return this.DeveloperTemplate;
}
if (item is Employee)
{
return this.EmployeeTemplate;
}
}
return null;
}
}
Step 3
Create a class MultiTemplateListBox
which will be derived from ListBox
. It will have a dependency property which will refer to TemplateSelector
to get the template for the given ListBoxItem
. We have to override the virtual method PrepareContainerForItemOverride
so that we can apply the template corresponding to the given ListBoxItem
. Here is the code for MultiTemplateListBox
.
public class MultiTemplateListBox : ListBox
{
public static readonly DependencyProperty TemplateSelectorProperty =
DependencyProperty.Register("TemplateSelector",
typeof(DataTemplateSelector), typeof(MultiTemplateListBox),
new PropertyMetadata(new PropertyChangedCallback(OnTemplateChanged)));
public DataTemplateSelector ItemTemplateSelector
{
get { return (DataTemplateSelector)this.GetValue(TemplateSelectorProperty); }
set { this.SetValue(TemplateSelectorProperty, value); }
}
protected override void PrepareContainerForItemOverride(
DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
ListBoxItem listBoxItem = element as ListBoxItem;
if (listBoxItem != null)
{
listBoxItem.ContentTemplate = this.ItemTemplateSelector.SelectTemplate(
item, this);
}
}
private static void OnTemplateChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
}
}
public class DataTemplateSelector
{
public virtual DataTemplate SelectTemplate(object item,
DependencyObject container)
{
return null;
}
}
Step 4
I created three different controls for three types of items. You can see the controls in the attached solution. Now, we can create the templates for each type of item, which will have different views for different types of items. Here is the code which defines the templates:
<DataTemplate x:Key="DeveloperTemplate">
<CustomControls:DeveloperView />
</DataTemplate>
<DataTemplate x:Key="LeaderTemplate">
<CustomControls:TeamLeaderView />
</DataTemplate>
<DataTemplate x:Key="ManagerTemplate">
<CustomControls:ManagerView />
</DataTemplate>
Step 5
Define an EmployeeTemplateSelector
in the User Control resource which will refer each template defined in the resources.
<CustomControls:EmployeeTemplateSelector x:Key="EmployeeTemplateSelector"
DeveloperTemplate=
"{StaticResource DeveloperTemplate}"
TeamLeaderTemplate=
"{StaticResource LeaderTemplate}"
ManagerTemplate=
"{StaticResource ManagerTemplate}"/>
Step 6
Include an object of MultiTemplateListBox
which will have a ItemTemplateSelector
property as a static resource and refers to the EmployeeTemplateSelector
key. Bind the ListBox
to the Employees
collection property.
<CustomControls:MultiTemplateListBox x:Name="EmployeeList"
ItemsSource="{Binding Employees}"
ItemTemplateSelector=
"{StaticResource EmployeeTemplateSelector}"
Width="800" Height="600"
Canvas.Top="20">
<ListBox.Template>
<ControlTemplate>
<Grid>D:\Personal\Submit Article\MultipleDataTemplateInListBox\
MultipleDataTemplateInListBox\EmployeeContainer.xaml
<ScrollViewer>
<ItemsPresenter />
</ScrollViewer>
</Grid>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<CustomControls:WrapPanel Width="Auto"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</CustomControls:MultiTemplateListBox>
The only thing remaining is to populate the data and set the context. Here is the code for it:
public class EmployeeContext
{
public List<employee> Employees { get; set; }
public EmployeeContext()
{
Employees = GetEmployeeList();
}
private List<employee> GetEmployeeList()
{
Developer dev1 = new Developer() { Name = "Mike", ManagerName = "Peterson" ,
ImageSource = @"/Images/image1.jpg"};
Developer dev2 = new Developer() { Name = "John", ManagerName = "Peterson" };
TeamLeader leader1 = new TeamLeader() { Name = "Peterson",
ManagerName = "Anderson", DirectReports = new List<employee> { dev1,
dev2 } };
Developer dev3 = new Developer() { Name = "Steave Mollenkopf",
ManagerName = "Tomi Swartz" };
Developer dev4 = new Developer() { Name = "Han Tran",
ManagerName = "Tomi Swartz" };
Developer dev5 = new Developer() { Name = "Parth Sarthi",
ManagerName = "Tomi Swartz" };
Developer dev6 = new Developer() { Name = "Shivank Nayak",
ManagerName = "Tomi Swartz" };
TeamLeader leader2 = new TeamLeader() { Name = "Allona Cholnika",
ManagerName = "Tomi Swartz", DirectReports = new List<employee> { dev3,
dev4, dev5, dev6 } };
Manager manager1 = new Manager() { Name = "Tomi Swartz",
DirectReports = new List<employee>() { leader1, leader2 } };
return new List<employee>()
{
dev1, dev2, dev3, dev4, dev5, dev6, leader1, leader2, manager1
};
}
}
EmployeeContext context = new EmployeeContext();
this.DataContext = context;
Now, if you run the code, you will find the items in three different colors (yellow, cyan, and green). I am not very good at UI designing, so the UI might not look very good. The ListBox
contains three different types of items, and different types of items have different UI. We can deal with them separately. Here, the team leader ListBoxItem
has a list of developers and the manager ListBoxItem
has a list of team leaders.
MultipleDataTemplateInListBox.zip
Comments
- Anonymous
April 21, 2009
PingBack from http://microsoft-sharepoint.simplynetdev.com/silverlight-listbox-part-i-using-multiple-templates-in-a-listbox/