列表控件

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. Asp.NetCore 中Aop的应用

    前言 其实好多项目中,做一些数据拦截.数据缓存都有Aop的概念,只是实现方式不一样:之前大家可能都会利用过滤器来实现Aop的功能,如果是Asp.NetCore的话,也可能会使用中间件: 而这种实现方式 ...

  2. com 组件的本知识

    (今日看到网络上关于"COM中GUID......"文章,写的好,故记录之.)当初微软设计com规范的时候,有两种选择来保证用户的设计的com组件可以全球唯一:第一种是采用和Int ...

  3. Qt 中的属性系统(Property System)

    21 人赞同了该文章 本节内容主要讲解我对 Qt 属性系统的理解.官方文档参考 The Property System. 如何理解"属性系统"这个概念? 一般我们说一个类有什么属性 ...

  4. js对url进行编码和解码

    编码 只有 0-9[a-Z] $ - _ . + ! * ' ( ) , 以及某些保留字,才能不经过编码直接用于 URL. 例如:搜索的中文关键字,复制网址之后再粘贴就会发现该URL已经被转码. 1. ...

  5. 【C#】 Stopwatch详解

    Stopwatch的命名空间是using System.Diagnostics; 1 namespace System.Diagnostics 2 { 3 // 4 // 摘要: 5 // 提供一组方 ...

  6. JDBC中的元数据

    在我编写JDBC代码的时候:出现很多的重复的代码,有没有什么办法让我们能够编写出更加通用的JDBC代码呢?使用元数据,元数据能够让我们编写出更加通用的JDBC代码.什么是元数据呢?(三种元数据)1)连 ...

  7. flink双流join

    package com.streamingjoin import org.apache.flink.api.common.state.{ValueState, ValueStateDescriptor ...

  8. ElasticSearch之配置文件详解

    ################################### Cluster ################################### #定义集群名称,默认是elasticse ...

  9. python进阶(20) 正则表达式的超详细使用

    正则表达式   正则表达式(Regular Expression,在代码中常简写为regex. regexp.RE 或re)是预先定义好的一个"规则字符率",通过这个"规 ...

  10. Mysql for Mac 安装及环境配置

    一.下载及安装 首先去官网下载mac对应版本的Mysql,尾缀为.dmg的程序包 下载地址:https://dev.mysql.com/downloads/mysql/ 下载完毕后,一步步傻瓜式安装即 ...