列表控件

ItemsControl为列表项控件定义了基本功能,下图是ItemsControl的继承关系:

在继承自ItemsControl类的层次结构中,还显示了项封装器(MenuItem、TreeVIewItem),因为这些项(类)可以包含自己的项集合(从而实现数控件和菜单的嵌套关系和层次结构)。在继承表中没有ComboBoxItem和ListBoxItem没有出现,那是因为他们不需要包含项的子集合,所以也就不继承自ItemsControl类。

ItemsControl类中雨格式化相关的属性:

名称 说明
ItemsSource 绑定的数据源(希望在列表中显示的集合或者DataView对象)
DisplayMemberPath 希望为每个数据项显示的属性。对于更复杂的表达形式或者为了使用多个属性的组合而言,应改用ItemTemplate(DataTemplate类型,也就是修改数据模板)属性
ItemStringFormat 格式化字符串,使用这个属性为每个项格式化文本。通常用在数字或者日期只转换成合适的显示形式
ItemContainerStyle 该属性是个样式。通过该样式可以设置封装每个项的容器的多个属性。容器取决于列表类型(ListBox类是ListBoxItem,ComboBox是ComboBoxItem),当填充列表是,自动创建这些封装器对象。其实就是修改项的样式
ItemContainerStyleSelector 使用代码为列表中每项的封装器选择样式的StyleSelector对象。可以使用该属性为列表中的不同项提供不同的样式。
AlternationCount 在数据中设置的交替集合数量。2:将在两个不同的行样式之间交替。3:将在三个不同的行样式之间交替
ItemTemplate 此类模板从绑定的对象提取合适的数据,并将提取的数据安排到合适的控件组合中(DataTemplate类型,数据模板
ItemTemplateSelector 使用代码为列表中的每个项选择模板的DataTemplateSelector对象。可以通过这个属性为不同的项应用不同的模板。
ItemsPanel 定义用于包含列表中项的面板。所有项封装器都添加到这个容器中。通常,使用垂直的VirtualizingStackPanel面板(能够改善性能,大数据量展示的时候。ListBox内部使用了,ItemsControl没有)
GroupStyle 如果使用分组,这个样式定义了应该如何格式化每个分组。当使用分组时,项封装器(ListBoxItem、ComboBoxItem等)被添加到表示每个分组的GroupItem封装器中,然后这些分组被添加到列表中。
GroupStyleSelector 使用代码为每个分组选择样式的StyleSelector对象,可以通过这个属性为每个不同的分组使用不同的样式

ItemsControl类继承层次中的下一层是Selector类,这个类为确定并设置选择项添加了属性,包括SelectedItem(选中的数据对象),SelectedIndex(选中项的位置),SelectedValue(所选数据对象的value属性,通过SelectedValuepath属性指定的)。注意:Selector类不支持多选(ListBox控件通过SelectionMode属性和SelectedItems属性来支持多选)。

并不是所有的ItemsControl都支持选择,对于ToolBar或者Menu控件,选择没有意义,所以只是继承ItemsControl,而不是继承Selector类。

列表样式

应用最多:列表项样式,如何应用交替行样式,如何根据指定的标准应用条件样式

ItemContainerStyle

通过属性ItemContainerStyle可以对列表项中的每个项应用同样的格式。当创建列表项时,列表控件就会将ItemContainerStyle属性向下传递给每个子项。对于ListBox控件,每个子项由ListBoxItem对象表示,对于ComboBox控件,每个子项由ComboBoxItem对象表示。也就是通过ListBox.ItemContainerStyle属性应用的样式都将用于设置每个ListBoxItem对象的属性。

<!--默认ListBoxItem样式-->
<Style x:Key="ListBoxItemBaseStyle" BasedOn="{StaticResource BaseStyle}" TargetType="ListBoxItem">
    <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisualRadius0Margin0}"/>
    <Setter Property="SnapsToDevicePixels" Value="True"/>
    <Setter Property="Padding" Value="10,0"/>
    <Setter Property="MinHeight" Value="{StaticResource DefaultControlHeight}"/>
    <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType=ItemsControl}}"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="Background" Value="{DynamicResource RegionBrush}"/>
    <Setter Property="BorderBrush" Value="Transparent"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListBoxItem">
                <Border x:Name="Bd" CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{Templ
                    <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="true">
            <Setter Property="Background" Value="{DynamicResource SecondaryRegionBrush}"/>
        </Trigger>
        <Trigger Property="IsSelected" Value="true">
            <Setter Property="Background" Value="{DynamicResource PrimaryBrush}"/>
            <Setter Property="Foreground" Value="{DynamicResource TextIconBrush}"/>
        </Trigger>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsSelected" Value="true"/>
                <Condition Property="Selector.IsSelectionActive" Value="false"/>
            </MultiTrigger.Conditions>
            <Setter Property="Background" Value="{DynamicResource DarkDefaultBrush}"/>
            <Setter Property="Foreground" Value="{DynamicResource PrimaryTextBrush}"/>
        </MultiTrigger>
        <Trigger Property="IsEnabled" Value="false">
            <Setter Property="Opacity" Value=".4"/>
        </Trigger>
        <Trigger Property="hc:EdgeElement.ShowEdgeContent" Value="true">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                            <StackPanel Orientation="Horizontal">
                                <ContentControl Width="16" Height="16" Content="{Binding Path=(hc:EdgeElement.LeftContent),RelativeSource={RelativeSource TemplatedParent}}"/>
                                <ContentPresenter Margin="6,0,0,0" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                            </StackPanel>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Trigger>
    </Style.Triggers>
</Style>
<Style x:Key="ListBoxBaseStyle" TargetType="ListBox">
    <Setter Property="Background" Value="{DynamicResource RegionBrush}"/>
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
    <Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="hc:ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
    <Setter Property="hc:ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
    <Setter Property="hc:ScrollViewer.CanContentScroll" Value="true"/>
    <Setter Property="hc:ScrollViewer.PanningMode" Value="Both"/>
    <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="ItemContainerStyle" Value="{StaticResource ListBoxItemBaseStyle}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListBox">
                <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true">
                    <hc:ToggleBlock IsChecked="{Binding HasItems,RelativeSource={RelativeSource TemplatedParent},Mode=OneWay}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch">
                        <hc:ToggleBlock.CheckedContent>
                            <hc:ScrollViewer Focusable="false">
                                <ItemsPresenter Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                            </hc:ScrollViewer>
                        </hc:ToggleBlock.CheckedContent>
                        <hc:ToggleBlock.UnCheckedContent>
                            <hc:Empty/>
                        </hc:ToggleBlock.UnCheckedContent>
                    </hc:ToggleBlock>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

包含复选框或单选按钮的ListBox控件

这种情况,我们分析一下得知:是将ListBox的每一项都改变,所以可以通过上面的ItemContainerStyle来修改每项的展示形式,也就是修改ListBoxItem的样式来实现。原来的内容展示变成了CheckBox或者RadioButton,可以通过修改控件模板的方式来改变每一项的展示形式。我们不需要修改ListBox的控件模板,而是修改ListBoxItem的控件模板(Template属性)。

<Style x:Key="CheckBoxListStyle" TargetType="{x:Type ListBox}">
    <Setter Property="SelectionMode" Value="Multiple" />
    <Setter Property="ItemContainerStyle">
        <Setter.Value>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="Margin" Value="2" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListBoxItem}">
                            <CheckBox IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}">
                                <ContentPresenter />
                            </CheckBox>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Setter.Value>
    </Setter>
</Style>
<Style x:Key="RadioButtonListStyle" TargetType="{x:Type ListBox}">
    <Setter Property="ItemContainerStyle">
        <Setter.Value>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="Margin" Value="2" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListBoxItem}">
                            <RadioButton Focusable="False" IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}">
                                <ContentPresenter />
                            </RadioButton>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Setter.Value>
    </Setter>
</Style>

这里有几点需要说明:

  • 上述代码能够正确展示,是因为RadioButton和CheckBox是内容控件,可以包含任何内容
  • 使用ContentPresenter元素作为内容控件的内容,能够获取最初在项中显示的内容,这些内容可以是简单的文本,也可以是复杂的数据表达式(如使用了ListBox.ItemTemplate属性)
  • RadioButton的IsChecked属性使用了绑定表达式,绑定到父模板的选中IsSelected属性上,能够呈现出RadioButton被选中时,ListBoxItem也就被选中了。
  • 同样,使用CheckBox元素时,需要将ListBox的SelectionMode属性设置成Multiple,已达到展示选中的效果

交替条目样式

交替行主要区分每两项的样式,一般通过稍微不同的背景颜色实现,从而清晰地隔离行。

交替行主要的两个属性是:AlternationCount(执行交替的行数,循环的个数,默认是0,不交替)、AlternationIndex(为每个Item提供的交替序列中的编号,使用触发器在ItemContainerStyle中检查这个编号并改变样式)

<ListBox Name="lstProducts" Margin="5" AlternationCount="2" DisplayMemberPath="ModelName">
    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="Background" Value="LightSteelBlue" />
            <Setter Property="Margin" Value="5" />
            <Setter Property="Padding" Value="5" />
            <Style.Triggers>
                <Trigger Property="ItemsControl.AlternationIndex" Value="1">
                    <Setter Property="Background" Value="LightBlue" />
                </Trigger>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="Background" Value="DarkRed" />
                    <Setter Property="Foreground" Value="White" />
                    <Setter Property="BorderBrush" Value="Black" />
                    <Setter Property="BorderThickness" Value="1" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

样式选择器

如果希望能够依赖于数据来决定ListBoxItem的样式,那么可以使用样式选择器,构建专门的集成自StyleSelector的类。该类负责检查每个数据项并选择合适的样式,该工作实在SelectStyle()方法中执行,需要重写这个方法。

public class SingleCriteriaHighlightStyleSelector : StyleSelector
    {
        public Style DefaultStyle
        {
            get;
            set;
        }
        public Style HighlightStyle
        {
            get;
            set;
        }
        public string PropertyToEvaluate
        {
            get;
            set;
        }
        public string PropertyValueToHighlight
        {
            get;
            set;
        }
        public override Style SelectStyle(object item,
          DependencyObject container)
        {
            Product product = (Product)item;
            // Use reflection to get the property to check.
            Type type = product.GetType();
            PropertyInfo property = type.GetProperty(PropertyToEvaluate);
            // Decide if this product should be highlighted
            // based on the property value.
            if (property.GetValue(product, null).ToString() == PropertyValueToHighlight)
            {
                return HighlightStyle;
            }
            else
            {
                return DefaultStyle;
            }
        }
    }

使用场景:

<ListBox Name="lstProducts" Grid.Row="1" Margin="7,3,7,10" 
         HorizontalContentAlignment="Stretch" SnapsToDevicePixels="True">
    <ListBox.ItemContainerStyleSelector>
        <local:SingleCriteriaHighlightStyleSelector DefaultStyle="{StaticResource DefaultStyle}" 
                                                    HighlightStyle="{StaticResource HighlightStyle}" 
                                                    PropertyToEvaluate="CategoryName" 
                                                    PropertyValueToHighlight="Travel" />
    </ListBox.ItemContainerStyleSelector>
</ListBox>

注意:样式选择过程只执行一次,当第一次绑定列表是执行。如果正在显示可编辑的数据,并且当进行编辑时可能将数据从一个样式类别移动到另一个样式类别中,这就成问题了。在这种情况下,需要强制WPF重新应用样式,粗鲁的方法就是将ItemContainerStyle属性设置成null,然后再次指定样式选择器。

也可以通过处理事件来响应特定修改,然后自动执行上述的过程。

我会采用一般的做法是刷新ItemsControl的数据来实现。

WPF进阶技巧和实战03-控件(5-列表、树、网格01)的更多相关文章

  1. WPF进阶技巧和实战03-控件(3-文本控件及列表控件)

    系列文章链接 WPF进阶技巧和实战01-小技巧 WPF进阶技巧和实战02-布局 WPF进阶技巧和实战03-控件(1-控件及内容控件) WPF进阶技巧和实战03-控件(2-特殊容器) WPF进阶技巧和实 ...

  2. WPF进阶技巧和实战03-控件(4-基于范围的控件及日期控件)

    系列文章链接 WPF进阶技巧和实战01-小技巧 WPF进阶技巧和实战02-布局 WPF进阶技巧和实战03-控件(1-控件及内容控件) WPF进阶技巧和实战03-控件(2-特殊容器) WPF进阶技巧和实 ...

  3. WPF进阶技巧和实战06-控件模板

    逻辑树和可视化树 System.Windows.LogicalTreeHelper System.Windows.Media.VisualTreeHelper 逻辑树类(LogicalTreeHelp ...

  4. WPF进阶技巧和实战07--自定义元素02

    在01节中,研究了如何开发自定义控件,下节开始考虑更特殊的选择:派生自定义面板以及构建自定义绘图 创建自定义面板 创建自定义面板是一种比较常见的自定义控件开发子集,面板可以驻留一个或多个子元素,并且实 ...

  5. WPF进阶技巧和实战09-事件(2-多点触控)

    多点触控输入 多点触控输入和传统的基于比的输入的区别是多点触控识别手势,用户可以移动多根手指以执行常见的操作,放大,旋转,拖动等. 多点触控的输入层次 WPF允许使用键盘和鼠标的高层次输入(例如单击和 ...

  6. WPF进阶技巧和实战03-控件(1-控件及内容控件)

    所有控件都继承自System.Windows.Controls.Control类,这个类添加一些基本结构: 设置控件内容对齐方式 (HorizontalContentAlignment,Vertica ...

  7. WPF进阶技巧和实战03-控件(5-列表、树、网格03)

    数据视图 数据视图是在后台工作的,用于协调绑定数据的集合.使用数据视图可以添加导航逻辑.实现数据过滤.排序.分组. 当将集合或者DataTable绑定到ItemsControl控件时,会不加通告地在后 ...

  8. WPF进阶技巧和实战03-控件(5-列表、树、网格02)

    数据模板 样式提供了基本的格式化能力,但是不管如何修改ListBoxItem,他都不能够展示功能更强大的元素组合,因为了每个ListBoxItem只支持单个绑定字段(通过DisplayMemberPa ...

  9. WPF进阶技巧和实战08-依赖属性与绑定03

    数据提供者 在大多数的代码中,都是通过设置元素的DataContext属性或者列表控件的ItemsSource属性,从而提供顶级的数据源.当数据对象是通过另一个类构造时,可以有其他选择. 一种是作为窗 ...

随机推荐

  1. 【小技巧】java的List分页

    今天,工作上,由于业务的一些特殊性,需要拿到数据后在java代码中进行分页. 写了一个工具类,记录如下: import java.util.ArrayList; import java.util.Li ...

  2. PQGrid商业化的表格组件

    官网地址https://paramquery.com/pro/grid 右侧导航条有目录哟,看着更方便 快速入门 表格构建 API简单介绍 主要研究功能介绍 快速入门 ParamQuery Grid ...

  3. jsp中核心标签使用

    <%@ page language="java" import="java.util.*, java.lang.*" pageEncoding=" ...

  4. 每天迁移MySQL历史数据到历史库Python脚本

    #!/usr/bin/env python # coding:utf-8 #__author__ = 'Logan'      import MySQLdb import sys import dat ...

  5. 聊聊spring事务失效的12种场景,太坑了

    前言 对于从事java开发工作的同学来说,spring的事务肯定再熟悉不过了. 在某些业务场景下,如果一个请求中,需要同时写入多张表的数据.为了保证操作的原子性(要么同时成功,要么同时失败),避免数据 ...

  6. 被面试官问懵:TCP 四次挥手收到乱序的 FIN 包会如何处理?

    摘要:收到个读者的问题,他在面试的时候,被搞懵了,因为面试官问了他这么一个网络问题. 本文分享自华为云社区<TCP 四次挥手收到乱序的 FIN 包会如何处理?>,作者:小林coding . ...

  7. Linux网络编程:原始套接字简介

    Linux网络编程:原始套接字编程 一.原始套接字用途 通常情况下程序员接所接触到的套接字(Socket)为两类: 流式套接字(SOCK_STREAM):一种面向连接的Socket,针对于面向连接的T ...

  8. TCP超时重传、序列号、滑动窗口简介

    文章目录 12 TCP:传输控制协议(初步) 12.1 引言 12.1.1 ARQ和重传 12.1.2 分组窗口和滑动窗口 12.1.3 变量窗口:流量控制和拥塞控制 12.1.4 变量窗口:设置重传 ...

  9. vue实现拖动div元素

    html: <div id="app1"> <div v-drag class="drag"></div> <div ...

  10. 了解HTTP基本知识板块

    一.HTTP 协议概述 HTTP协议采用了请求/响座模型. 客户端向服务器发送-个请求,请求头包含请求的方法.URT..协议版本.以以 及包含请求修饰符.客户信息和内容的类似于MIME的消息结构. 服 ...