WPF列表样式

WPF的数据绑定系统自动生成列表项对象,为单个项应用所需的样式不是很容易。解决方案是ItemContainerStyle 属性。如果设置了ItemContainerStyle 属性,当创建列表项时,列表控件会将其向下传递给每个项。对于ListBox控件,每个项有ListBoxItem 对象表示,对于CombBox 控件,则对应是 CombBoxItem。

交替条目样式

WPF通过两个属性为交替项提供内置支持:AlternationCount 和 AlternationIndex。

cs 复制代码
<Window>
    <Window.Resources>
        <Style x:Key="listBoxItemStyle" TargetType="{x:Type ListBoxItem}">
            <Setter Property="Background" Value="LightBlue"/>
            <Setter Property="Margin" Value="5"></Setter>
            <Setter Property="Padding" Value="5"></Setter>
            <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="10"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid x:Name="myGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition MinHeight="100"/>
        </Grid.RowDefinitions>
        <ListBox Grid.Row="0" Grid.Column="0" ItemContainerStyle="{StaticResource listBoxItemStyle}" ItemsSource="{Binding Path=Orders}" AlternationCount="2" DisplayMemberPath="Price"/>
    </Grid>
</Window>

也可以直接将样式设置到ListBox层次

cs 复制代码
<Window>
    <Window.Resources>
        <Style x:Key="checkBoxListStyle" TargetType="{x:Type ListBox}">
            <Setter Property="SelectionMode" Value="Multiple"></Setter>
            <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>
    </Window.Resources>
    <Grid x:Name="myGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition MinHeight="100"/>
        </Grid.RowDefinitions>
        <ListView Grid.Row="1" Grid.Column="0" Style="{StaticResource checkBoxListStyle}" ItemsSource="{Binding Path=Orders}" DisplayMemberPath="Price" Name="checkButtonListBox"/>
    </Grid>
</Window>

样式选择器

可以使用样式选择器来为不同的子项提供不同的样式,自定义样式选择器需要继承自 StyleSelector 类,需要重写 SelectStyle() 方法。

cs 复制代码
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)
    {
        Order order = (Order)item;
        if (order.Price > 1000)
        {
            return HighlightStyle;
        }
        else
        {
            return DefaultStyle;
        }
    }
}

完整的代码文件:

MainWindow.xaml

cs 复制代码
<Window x:Class="ListBoxStyle.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ListBoxStyle"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <Style x:Key="listBoxItemStyle" TargetType="{x:Type ListBoxItem}">
            <Setter Property="Background" Value="Blue"/>
            <Setter Property="Margin" Value="5"></Setter>
            <Setter Property="Padding" Value="5"></Setter>
            <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="10"/>
                </Trigger>
            </Style.Triggers>
        </Style>
        <Style x:Key="radioButtonListStyle" TargetType="{x:Type ListBoxItem}">
            <Setter Property="Background" Value="Blue"/>
            <Setter Property="Margin" Value="5"></Setter>
            <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>
        <Style x:Key="checkBoxListStyle" TargetType="{x:Type ListBox}">
            <Setter Property="SelectionMode" Value="Multiple"></Setter>
            <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="DefaultStyle" TargetType="{x:Type ListBoxItem}">
            <Setter Property="Background" Value="LightYellow" />
            <Setter Property="Padding" Value="2" />
        </Style>
        <Style x:Key="HighlightStyle" TargetType="{x:Type ListBoxItem}">
            <Setter Property="Background" Value="LightSteelBlue" />
            <Setter Property="FontWeight" Value="Bold" />
            <Setter Property="Padding" Value="2" />
        </Style>
    </Window.Resources>
    <Grid x:Name="myGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition MinHeight="100"/>
        </Grid.RowDefinitions>
        <ListBox Grid.Row="0" Grid.Column="0" ItemContainerStyle="{StaticResource listBoxItemStyle}" ItemsSource="{Binding Path=Orders}" AlternationCount="3" DisplayMemberPath="Price"/>
        <ListBox Grid.Row="0" Grid.Column="1" ItemContainerStyle="{StaticResource radioButtonListStyle}" ItemsSource="{Binding Path=Orders}" DisplayMemberPath="Price" Name="radioButtonListBox"/>
        
        <ListView Grid.Row="1" Grid.Column="0" Style="{StaticResource checkBoxListStyle}" ItemsSource="{Binding Path=Orders}" DisplayMemberPath="Price" Name="checkButtonListBox"/>
        <ListBox Grid.Row="1" Grid.Column="1" ItemsSource="{Binding Path=Orders}" DisplayMemberPath="Price" Name="styleSelectorListBox">
            <ListBox.ItemContainerStyleSelector>
                <local:SingleCriteriaHighlightStyleSelector DefaultStyle="{StaticResource DefaultStyle}" HighlightStyle="{StaticResource HighlightStyle}"></local:SingleCriteriaHighlightStyleSelector>
            </ListBox.ItemContainerStyleSelector>
        </ListBox>
        
        <Button Grid.Row="4" Click="Button_Click">Test</Button>
        <Button Grid.Row="4" Grid.Column="1" Click="Button_Click_1">Test</Button>
    </Grid>
</Window>

MainWindow.xaml.cs

cs 复制代码
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ListBoxStyle;

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    protected virtual bool SetProperty<T>(ref T member, T value, [CallerMemberName] string? propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(member, value))
        {
            return false;
        }
        member = value;
        OnPropertyChanged(propertyName);
        return true;
    }
}
public class Order : ViewModelBase
{
    public decimal price = 0;
    public decimal Price { get => price; set => SetProperty(ref price, value); }
    public int volume = 0;
    public int Volume { get => volume; set => SetProperty(ref volume, value); }

    public DateTime orderDate = DateTime.Now;
    public DateTime OrderDate { get => orderDate; set => SetProperty(ref orderDate, value); }

    public string image = string.Empty;
    public string Image { get => image; set => SetProperty(ref image, value); }
}

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)
    {
        Order order = (Order)item;
        if (order.Price > 1000)
        {
            return HighlightStyle;
        }
        else
        {
            return DefaultStyle;
        }
    }
}



public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        myGrid.DataContext = this;

        Order order1 = new Order();
        Order order2 = new Order();
        Order order3 = new Order();
        Order order4 = new Order();

        order1.Price = 100;
        order1.Volume = 10;

        order2.Price = 1000;
        order2.Volume = 100;

        order3.Price = 10000;
        order3.Volume = 1000;

        order4.Price = 100000;
        order4.Volume = 10000;

        Orders.Add(order1);
        Orders.Add(order2);
        Orders.Add(order3);
        Orders.Add(order4);
    }

    public ObservableCollection<Order> Orders {get; set;} = new ();

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        string message = "";
        if(radioButtonListBox.SelectedItem != null)
        {
            Order order = (Order)radioButtonListBox.SelectedItem;
            message = order.Price.ToString();
        }
        message += "\n";
        foreach (var selectedItem in checkButtonListBox.SelectedItems)
        {
            Order order = (Order)selectedItem;
            message += order.Price.ToString() + " ";
        }
        MessageBox.Show(message);
    }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        Orders[1].Price = 50000;
        StyleSelector selector = styleSelectorListBox.ItemContainerStyleSelector;
        styleSelectorListBox.ItemContainerStyleSelector = null;
        styleSelectorListBox.ItemContainerStyleSelector = selector;

    }
}
相关推荐
滴_咕噜咕噜11 小时前
学习笔记(prism--视频【WPF-prism核心教程】)--待更新
笔记·学习·wpf
别给迷住了16 小时前
WPF 绘制过顶点的圆滑曲线(样条,贝塞尔)
wpf·贝塞尔曲线·样条曲线·圆滑曲线·过顶点·beziersegment
白露与泡影1 天前
Redisson分布式锁的源码解读
分布式·wpf
勇者神龟1 天前
.net framework wpf 打包免安装exe文件
.net·wpf
吉量*2 天前
WPF系列四:图形控件Rectangle
wpf
假男孩儿2 天前
WPF 最小化到系统托盘
wpf
勇敢小菜鸟3 天前
WPF自定义窗口 输入验证不生效
wpf
鲤籽鲲3 天前
WPF TextBox 输入限制 详解
wpf
鸿喵小仙女3 天前
C# WPF读写STM32/GD32单片机Flash数据
stm32·单片机·c#·wpf
六点的晨曦3 天前
WPF的右键菜单项目引入DLL和DllImport特性引入DLL文件的异同点
wpf