Silverlight ListBox:第 1 部分 -在 ListBox 中使用多个模板






4.79/5 (10投票s)
如何使用多个模板在单个 ListBox 中显示多种类型的项目。
引言
ListBox 是任何技术(如 ASP.NET 或 Silverlight)中一个非常重要的控件。通常,我们使用它来显示单行记录,用户可以选择其中一个。我将不讨论如何在 Silverlight 中使用 ListBox,因为互联网上有很多关于该主题的文章。在这篇文章中,我将讨论如何使用模板来显示 ListBox 中的项目。尽管有许多关于在 ListBox 中使用单个模板的文章,但很少有文章讨论在 ListBox 中使用多个模板。有时我们可能需要显示具有一些共同属性和一些特定属性的项目,并且我们可能希望在同一个 ListBox 中为每种类型的项目显示不同的 UI。
让我们举个例子来更好地理解。
这是一个非常常见的场景。有一个组织,其中有软件开发人员、团队领导和经理。他们都有共同的属性,如 EmployeeName 和 ManagerName,但团队领导和经理将直接向他们汇报。
我将创建一个 ListBox,其中将包含软件开发人员、团队领导和经理。每个人都将拥有 Employee Name 和 Manager Name 等共同属性。团队领导和经理项目将拥有一个 ListBox,其中包含一个报告者列表。以下是我用于区分每种项目类型的背景颜色列表。
项目类型 | 背景颜色 |
软件开发人员 | 黄色 |
团队领导 | 青色 |
管理器 | 绿色 |
背景
有一个关于 ListBox 的非常好的示例,它使用 Panel
作为 ListBox 的容器。通常,我们使用 ListBox 并将 ListBox 项目垂直排列,即使有足够的水平空间。此示例为 ListBox 实现了一个 WrapPanel;如果您使用的是 WrapPanel,则无需担心 ListBox 项目的位置。如果水平空间足够,则同一行可以渲染多个项目,如果下一个项目没有足够的空间,则下一个项目将在下一行渲染。除此之外,它还负责 ListBox 的调整大小,因此如果最终用户正在调整 ListBox 的大小,项目将重新渲染并采用对应可用空间的新位置。此示例还描述了一个动画,但我不会在我的示例中使用它。以下是文章链接:
您可以直接按照我的示例进行操作,即使不阅读上述文章,但我强烈建议阅读它。
核心思想是什么?
以下是我们为实现目标将遵循的步骤。在代码部分,这些步骤将更加清晰。
- 创建一个模板类,其中包含 ListBox 中所有可能模板的属性。
- 创建一个新类,该类将从
ListBox
派生。它将有一个新的依赖属性来为给定的ListBoxItem
选择模板。重写PrepareContainerForItemOverride
方法,因为它实际上会准备指定的元素来显示给定的ListBoxItem
。它应该调用基类(ListBox
)的PrepareContainerForItemOverride
方法,并从TemplateSelecter
属性设置项的模板。 - 在 XAML 资源中定义每种项目类型的模板。
- 在 XAML 资源中定义一个
TemplateSelecter
,它将引用ListBox
中项目类型的 DataTemplate。 - 在用户控件 XAML 的 ListBox 声明中引用此
TemplateSelector
。
Using the Code
步骤 1
定义每种项目类型的类。所有类都应具有一个共同的基类。这里,Employee
是共同的基类。
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; }
}
第二步
创建一个 EmployeeTemplateSelector
类,该类应包含 ListBox
中使用的所有模板,并具有一个 SelectTemplate
方法,该方法将返回模板。
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;
}
}
步骤 3
创建一个名为 MultiTemplateListBox
的类,它将从 ListBox
派生。它将有一个依赖属性,该属性将引用 TemplateSelector
以获取给定 ListBoxItem
的模板。我们必须重写虚拟方法 PrepareContainerForItemOverride
,以便我们可以应用与给定 ListBoxItem
对应的模板。以下是 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;
}
}
步骤 4
我为三种类型的项目创建了三个不同的控件。您可以在附件的解决方案中看到这些控件。现在,我们可以为每种类型的项目创建模板,它们将为不同类型的项目提供不同的视图。以下是定义模板的代码:
<DataTemplate x:Key="DeveloperTemplate">
<CustomControls:DeveloperView />
</DataTemplate>
<DataTemplate x:Key="LeaderTemplate">
<CustomControls:TeamLeaderView />
</DataTemplate>
<DataTemplate x:Key="ManagerTemplate">
<CustomControls:ManagerView />
</DataTemplate>
步骤 5
在用户控件资源中定义一个 EmployeeTemplateSelector
,它将引用资源中定义的每个模板。
<CustomControls:EmployeeTemplateSelector x:Key="EmployeeTemplateSelector"
DeveloperTemplate=
"{StaticResource DeveloperTemplate}"
TeamLeaderTemplate=
"{StaticResource LeaderTemplate}"
ManagerTemplate=
"{StaticResource ManagerTemplate}"/>
步骤 6
包含 MultiTemplateListBox
的一个实例,它将具有一个 ItemTemplateSelector
属性作为静态资源,并引用 EmployeeTemplateSelector
键。将 ListBox
绑定到 Employees
集合属性。
<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>
唯一剩下的就是填充数据并设置上下文。以下是相关代码:
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;
现在,如果您运行代码,您会发现这些项目具有三种不同的颜色(黄色、青色和绿色)。我对 UI 设计不太擅长,所以 UI 可能看起来不太好。ListBox
包含三种不同类型的项目,而不同类型的项目具有不同的 UI。我们可以分别处理它们。在这里,团队领导的 ListBoxItem
包含一个开发人员列表,经理的 ListBoxItem
包含一个团队领导列表。