65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.79/5 (10投票s)

2009年2月26日

CPOL

5分钟阅读

viewsIcon

90331

downloadIcon

965

如何使用多个模板在单个 ListBox 中显示多种类型的项目。

listbox.jpg

引言

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 的大小,项目将重新渲染并采用对应可用空间的新位置。此示例还描述了一个动画,但我不会在我的示例中使用它。以下是文章链接:

您可以直接按照我的示例进行操作,即使不阅读上述文章,但我强烈建议阅读它。

核心思想是什么?

以下是我们为实现目标将遵循的步骤。在代码部分,这些步骤将更加清晰。

  1. 创建一个模板类,其中包含 ListBox 中所有可能模板的属性。
  2. 创建一个新类,该类将从 ListBox 派生。它将有一个新的依赖属性来为给定的 ListBoxItem 选择模板。重写 PrepareContainerForItemOverride 方法,因为它实际上会准备指定的元素来显示给定的 ListBoxItem。它应该调用基类(ListBox)的 PrepareContainerForItemOverride 方法,并从 TemplateSelecter 属性设置项的模板。
  3. 在 XAML 资源中定义每种项目类型的模板。
  4. 在 XAML 资源中定义一个 TemplateSelecter,它将引用 ListBox 中项目类型的 DataTemplate。
  5. 在用户控件 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 包含一个团队领导列表。

© . All rights reserved.