WPF中文网

ListBox的ItemContainerStyle

众所周知,ListBox继承于ItemsControl控件,那么,它就与ItemsControl一样,拥有了可以设置的数据模板。当然,它也可以拥有自己的控件模板(在Control基类中定义的Template)。这一节,我们只探讨一下ListBox如何使用数据模板。

我们可以将上一章节中的ItemsControl直接改成ListBox。

<ListBox ItemsSource="{Binding Persons}" >
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border x:Name="border"
                    Width="280"
                    Height="200"
                    Margin="5"
                    BorderThickness="1" 
                    BorderBrush="Gray">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <StackPanel Grid.Row="0" Margin="20">
                        <TextBlock Text="{Binding Name}" FontWeight="Bold" FontSize="20"/>
                        <Rectangle Height="5"/>
                        <TextBlock Text="{Binding Occupation}" FontSize="16"/>
                    </StackPanel>
                    <StackPanel Grid.Row="1" Orientation="Horizontal">
                        <TextBlock Grid.Column="0" Text="☻"  
                                       VerticalAlignment="Center"  Margin="20" 
                                       FontSize="50" Foreground="#E26441"/>
                        <StackPanel Margin="30 0 0 0" Width="150">
                            <TextBlock Text="COMPANY NAME"/>
                            <TextBlock Text="Age:">
                                    <Run Text="{Binding Age}"/>
                            </TextBlock>
                            <TextBlock Text="Money:">
                                    <Run Text="{Binding Money, StringFormat={}{0:C}}"/>
                            </TextBlock>
                            <TextBlock Text="Address:" TextWrapping="Wrap">
                                    <Run Text="{Binding Address}"/>
                            </TextBlock>
                        </StackPanel>
                    </StackPanel>
                </Grid>
            </Border>
            <DataTemplate.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="#7AAB7D" TargetName="border" />
                </Trigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ListBox>

因为ListBox是ItemsControl的子类,所以,这样的修改是没有问题的。只不过,如上图所示,在每个元素的外围,当鼠标移上去时,会出现一个淡蓝色的边框区域,这是为何呢?

这是因为在ListBox的父类ItemsControl中定义了一个ItemContainerStyle的样式,这个样式决定了ListBox控件中每个元素的容器外观。原来,在集合控件中,并不是说将一堆元素直接丢到里面呈现,而是先给每个元素分配一个容器,再将它们呈现在集合控件中。就好比给每个学生发一套校服,穿好后再规规距距地坐在教室里。

既然如此,那我们就可以给每个学生重新发一套校服,或者干脆不穿校服——毕竟每个学生自己都穿了衣服的(数据模板)。

<ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ListBoxItem">
                    <ContentPresenter/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ListBox.ItemContainerStyle>

ItemContainerStyle的Template的内容必须是ControlTemplate (控件模板)。这里同样使用了ContentPresenter,我们已然在前面讲过,这里指的是,将来由每个元素进行替换。注意TargetType是ListBoxItem类型。因为这个校服的品牌方就是指ListBox的ListBoxItem元素。

如果我们要给每个学生穿一件金黄色的衣服,如下所示

<ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ListBoxItem">
                    <Border Background="LightGoldenrodYellow" 
                            Padding="15" Margin="5">
                        <ContentPresenter/>
                    </Border>                                
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ListBox.ItemContainerStyle>

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:068-《ListBox的ItemContainerStyle》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

什么是数据模板?其实就是数据的表现形式——数据外衣。

public class DataTemplate : FrameworkTemplate
{
    public DataTemplate();
    public DataTemplate(object dataType);

    public object DataType { get; set; }
    public TriggerCollection Triggers { get; }
    public object DataTemplateKey { get; }

    protected override void ValidateTemplatedParent(FrameworkElement templatedParent);

}

DataTemplate 继承于FrameworkTemplate基类,它有3个属性,分别是DataType 、Triggers 和DataTemplateKey 。DataType表示当前数据模板所针对的数据类型,Triggers 是触发器集合。

在ItemsControl集合控件中就有一个ItemTemplate属性,它的类型就是DataTemplate 。说明所有继承于ItemsControl的集合子控件都可以设置数据模板。

接下来,我们以ItemsControl控件为例,演示其数据模块的使用方法。

我们将上一章节的例子稍做修改,首先是MainViewModel,新建一个Person的集合作为ItemsControl控件的数据源。

public class MainViewModel : ObservableObject
{
    private List<Person> persons = new List<Person>();
    public List<Person> Persons
    {
        get { return persons; }
        set { persons = value;RaisePropertyChanged(); }
    }

    private Person person;
    public Person Person
    {
        get { return person; }
        set { person = value; RaisePropertyChanged(); }
    }
    public MainViewModel()
    {
        person = new Person()
        {
            Name = "Michael Jackson",
            Occupation = "Musicians",
            Age = 25,
            Money = 9999999,
            Address = "深圳市光明区智慧招商城B4栋5楼"
        };

        var bill = new Person()
        {
            Name = "比尔·盖茨(Bill Gates)",
            Occupation = "微软公司创始人",
            Age = 61,
            Money = 9999999,
            Address = "美国华盛顿州西雅图"
        };

        var musk = new Person()
        {
            Name = "Elon Reeve Musk",
            Occupation = "首席执行官",
            Age = 50,
            Money = 365214580,
            Address = "出生于南非的行政首都比勒陀利亚"
        };

        var jeff = new Person()
        {
            Name = "杰夫·贝索斯(Jeff Bezos)",
            Occupation = "董事会执行主席",
            Age = 25,
            Money = 85745845,
            Address = "杰夫·贝索斯出生于美国新墨西哥州阿尔布奎克。"
        };

        persons.Add(person);
        persons.Add(bill);
        persons.Add(musk);
        persons.Add(jeff);
    }
}

然后在XAML前端代码中实例化一个ItemsControl控件,并将其包含在ScrollViewer之中。

<Grid>
    <ScrollViewer>
        <ItemsControl ItemsSource="{Binding Persons}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border x:Name="border"
                        Width="280"
                        Height="200"
                        Margin="5"
                        BorderThickness="1" 
                        BorderBrush="Gray">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <StackPanel Grid.Row="0" Margin="20">
                                <TextBlock Text="{Binding Name}" FontWeight="Bold" FontSize="20"/>
                                <Rectangle Height="5"/>
                                <TextBlock Text="{Binding Occupation}" FontSize="16"/>
                            </StackPanel>
                            <StackPanel Grid.Row="1" Orientation="Horizontal">
                                <TextBlock Grid.Column="0" Text="☻"  
                                           VerticalAlignment="Center"  Margin="20" 
                                           FontSize="50" Foreground="#E26441"/>
                                <StackPanel Margin="30 0 0 0" Width="150">
                                    <TextBlock Text="COMPANY NAME"/>
                                    <TextBlock Text="Age:">
                                        <Run Text="{Binding Age}"/>
                                    </TextBlock>
                                    <TextBlock Text="Money:">
                                        <Run Text="{Binding Money, StringFormat={}{0:C}}"/>
                                    </TextBlock>
                                    <TextBlock Text="Address:" TextWrapping="Wrap">
                                        <Run Text="{Binding Address}"/>
                                    </TextBlock>
                                </StackPanel>
                            </StackPanel>
                        </Grid>
                    </Border>
                    <DataTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Background" Value="#7AAB7D" TargetName="border" />
                        </Trigger>
                    </DataTemplate.Triggers>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </ScrollViewer>    
</Grid>

注意:ScrollViewer外面不能是StackPanel,所以这里改成Grid之后,ItemsControl的滚动条效果才会起效。起初我们没有设置ItemsControl 的ItemTemplate(数据模板),只是绑定了数据源,此时它的效果是下面这样子的。

然后,我们给ItemTemplate属性实例化了一个DataTemplate,其视觉树的样式参考前一节内容。它的效果如下

由此可见,ItemsControl的元素默认是垂直排列的,因为用于指定元素排列的默认布局控件是StackPanel。如果我们希望改变元素的排列布局方向,则需要修改ItemsControl控件的ItemsPanel属性——即ItemsPanelTemplate元素布局模板。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:066-《DataTemplate数据模板》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年10月7日

copyright @重庆教主 WPF中文网 联系站长:(QQ)23611316 (微信)movieclip (QQ群).NET小白课堂:864486030 | 本文由WPF中文网原创发布,谢绝转载 渝ICP备2023009518号-1