WPF中ListView相关问题,ListView背景颜色交替(奇偶行不同颜色),ListView标题填充

一、ListView样式和模板

    ListView的模板由三部分组成,分别为Thumb、GridViewColumnHeader、ListViewItem。其中Thumb在GridViewColumnHeader的模板中,参考以下自定义模板:

    1、GridViewColumnHeader

<Style  TargetType="{x:Type GridViewColumnHeader}">
        <Setter Property="OverridesDefaultStyle" Value="True"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type GridViewColumnHeader}">
                    <Grid Name="g" Background="#1578F6" >

                        <Border Name="bd" Padding="{TemplateBinding Padding}" MaxHeight="{TemplateBinding Height}">
                            <!--<ContentPresenter Margin="5,4,1,3" 
                                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>-->
                            <ContentPresenter RecognizesAccessKey="True" 
                                          Content="{TemplateBinding ContentControl.Content}" 
                                          ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" 
                                          ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" 
                                          Name="HeaderContent" Margin="5,4,1,3" 
                                           
                                           
                                          SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
                        </Border>
                        <Thumb Name="PART_HeaderGripper" Template="{StaticResource tmpForThumb}" HorizontalAlignment="Right" Margin="0,0,-1,0"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="g" Property="Background" Value="#0A6CE7">

                            </Setter>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter Property="Margin" Value="6,5,1,3" TargetName="HeaderContent"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    其中的<Thumb Name="PART_HeaderGripper" Template="{StaticResource tmpForThumb}" HorizontalAlignment="Right" Margin="0,0,-1,0"/>绑定下面的Thumb模板,实现表头标题之间互相伸缩。

    2、Thumb

<ControlTemplate x:Key="tmpForThumb" TargetType="{x:Type Thumb}">
        <Border>
            <Border BorderThickness="1 0 1 0"  Name="rec" BorderBrush="Transparent">
            </Border>
        </Border>
    </ControlTemplate>

    3、ListViewItem

<Style TargetType="{x:Type ListViewItem}">
        <Style.Setters>
            <Setter Property="Background">
                <Setter.Value>
                    <Binding RelativeSource="{RelativeSource Self}" 
                             Converter="{StaticResource myConverter}"/>
                </Setter.Value>
            </Setter>
            <Setter Property="SnapsToDevicePixels" Value="True"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListViewItem}">
                        <Border Padding="{TemplateBinding Control.Padding}"  
                                Background="{TemplateBinding Panel.Background}" 
                                Name="Bd" SnapsToDevicePixels="True">
                            <GridViewRowPresenter HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" 
                                                  VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}" 
                                                  SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"  
                                                  Margin="3"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsSelected" Value="False"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="Foreground" Value="#000000"/>
                            </MultiTrigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="UIElement.IsMouseOver" Value="True">
                                    </Condition>
                                </MultiTrigger.Conditions>
                                <Setter Property="Panel.Background" TargetName="Bd">
                                    <Setter.Value>
                                        <SolidColorBrush>#D9DFE8</SolidColorBrush>
                                    </Setter.Value>
                                </Setter>
                            </MultiTrigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="Selector.IsSelectionActive" Value="False">
                                    </Condition>
                                    <Condition Property="Selector.IsSelected" Value="True">
                                    </Condition>
                                </MultiTrigger.Conditions>
                                <Setter Property="Panel.Background" TargetName="Bd">
                                    <Setter.Value>
                                        <SolidColorBrush>#3DDADADA</SolidColorBrush>
                                    </Setter.Value>
                                </Setter>

                            </MultiTrigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="Selector.IsSelectionActive" Value="True">
                                    </Condition>
                                    <Condition Property="Selector.IsSelected" Value="True">
                                    </Condition>
                                </MultiTrigger.Conditions>
                                <Setter Property="Panel.Background" TargetName="Bd">
                                    <Setter.Value>
                                        <SolidColorBrush>#3D26A0DA</SolidColorBrush>
                                    </Setter.Value>
                                </Setter>

                            </MultiTrigger>
                            <Trigger Property="IsSelected" Value="True">
                                <Trigger.Setters>
                                    <Setter TargetName="Bd" Property="Background" Value="#8CB1E0"/>
                                    <Setter Property="Foreground" Value="#FFFFFF"/>
                                </Trigger.Setters>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style.Setters>
    </Style>

    ListViewItem中需要注意的是模板中使用到的是ListViewRowPresenter这个内容控件。

    其中下面这段代码是用来设置列表背景间隔色,如果不用可以直接删除这段代码,因为绑定到一个自定义属性,没有C#代码这里会报错,会在下面的背景颜色交替部分详细讲解。 

<Setter Property="Background">
    <Setter.Value>
        <Binding RelativeSource="{RelativeSource Self}" 
                 Converter="{StaticResource myConverter}"/>
    </Setter.Value>
</Setter>

    将上面三段代码复制到资源里字典里后,由于没有给定Name,模板会自动应用在引用这个资源字典里所有的ListView上。效果如图:

WPF ListBox启用虚拟化_wpf


二、ListView背景颜色交替(奇偶行不同颜色)

    1、添加一个BackgroundConverter的cs文件,继承IValueConverter接口,复制下面代码:

class BackgroundConverter: IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            ListViewItem item = (ListViewItem)value;
            ListView listView =
                ItemsControl.ItemsControlFromItemContainer(item) as ListView;
            // Get the index of a ListViewItem
            int index =
                listView.ItemContainerGenerator.IndexFromContainer(item);

            if (index % 2 == 0)
            {
                return Brushes.LightSteelBlue;
            }
            else
            {
                return Brushes.White;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    一般继承IValueConverter后会提示生成Convert和ConvertBack两个方法,其中需要注意的是Brushes类是System.Windows.Media这个名称空间的。我将这个BackgroundConverter类保存到Contorl文件夹里,在下面引用时需要注意。

    2、在ListView的资源字典里引用

xmlns:c="clr-namespace:demo.Control"

    并且在<ResourceDictionary>控件中添加以下代码,将BackgroundConverter作为资源使用:

<c:BackgroundConverter x:Key="myConverter"/>

    3、最后在ListViewItem样式中添加以下代码,也就是上面的那段代码

<Setter Property="Background">
    <Setter.Value>
        <Binding RelativeSource="{RelativeSource Self}" 
                 Converter="{StaticResource myConverter}"/>
    </Setter.Value>
</Setter>

    效果如图:

WPF ListBox启用虚拟化_WPF ListBox启用虚拟化_02

三、ListView标题填充

    由于GridViewColumn并不能设置Width="*",达不到填充的效果,需要自定义Width的大小,但往往布局看起来就不好看了,这是使用了网上的方法,连接忘记收藏了。使用Grid控件来设置Width="*",然后跟GridViewColumn的Width绑定以实现填充的效果。复制下面代码,需要注意的是Grid的Visibility要设置Hidden属性,隐藏起来。

<!--This is the hidden helper Grid which does the resizing -->
<Grid Visibility="Hidden" Grid.Column="1">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Name="re_col1" Width="{Binding ElementName=col1, Path=ActualWidth}"/>
        <ColumnDefinition Name="col2" Width="*"/>
        <ColumnDefinition Width="10"/>
    </Grid.ColumnDefinitions>
    <!--This is the hidden helper Field which is used to bind to, using the "Fill" column of the helper grid-->
    <Grid Grid.Column="1" x:Name="helperField"/>
</Grid>

    而在原来的ListView写入以下代码:

<ListView x:Name="lvTestListViewStyle" Foreground="#FFFFFF">
    <ListView.View>
        <GridView x:Name="gv" AllowsColumnReorder="True">
            <GridViewColumn x:Name="col1" DisplayMemberBinding="{Binding Path=Name}" Header="姓名" Width="100"/>
            <GridViewColumn DisplayMemberBinding="{Binding Path=Phone}" Header="联系电话" 
                            Width="{Binding ElementName=col2, Path=ActualWidth}"/>
        </GridView>
    </ListView.View>
</ListView>

    但经过我测试,这个方法很有局限,在窗口初始时如果不是最大化的状态可以很完好实现表头填充的效果,但一旦窗口最大化表头并不会跟着窗口变大而变化,而是会保持原来的宽度。如果窗口没做最大化的功能能避免这个问题,或者窗口初始化就是最大化的状态也可以避免这个问题,就是有点蠢。

    效果如图:

WPF ListBox启用虚拟化_前端_03