WPF ListView与ListBox(选中)项自定义样式
想用ListBox或者ListView控件,做一个菜单栏等,却发现很难改变ListBoxItem/ListViewItem项在鼠标移过、选中的默认蓝色背景与边框。
首先尝试使用控件的Style来设置ItemTemplate上的Triggers,结果时不成功。
经过查资料发现很少,最后在自定义List项时,发现对ListBoxItem/ListViewItem进行样式设计可以改变项的样式。具体实现如下所示。
ListViewItem
废话不说,我们首先上代码。
1.在资源模块中定义好我们的Item样式
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="0" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="0" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Item项相关颜色 -->
<SolidColorBrush x:Key="Item.MouseOver.Background" Color="#DADADA"/>
<SolidColorBrush x:Key="Item.MouseOver.Border" Color="#3DDADADA"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Background" Color="#FF0B7AFF"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Border" Color="#3DDADADA"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Foreground" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="Item.SelectedActive.Background" Color="#FF0B7AFF"/>
<SolidColorBrush x:Key="Item.SelectedActive.Border" Color="#FF0B7AFF"/>
<SolidColorBrush x:Key="Item.SelectedActive.Foreground" Color="#FFFFFFFF"/>
<!-- Item项样式 -->
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Padding" Value="4,1"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentControl x:Name="Ct" Foreground="{TemplateBinding Foreground}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</ContentControl>
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.MouseOver.Border}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="False"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Border}"/>
<Setter Property="Foreground" TargetName="Ct" Value="{StaticResource Item.SelectedInactive.Foreground}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="True"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
<Setter Property="Foreground" TargetName="Ct" Value="{StaticResource Item.SelectedActive.Foreground}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
2.样式应用
<ListView Margin="2,5"
ItemsSource="{Binding Path=MelphiDataSource,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}">
<ListView.Template>
<ControlTemplate TargetType="ListView">
<Border CornerRadius="15"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<Border.OpacityMask>
<VisualBrush>
<VisualBrush.Visual>
<Border SnapsToDevicePixels="True"
Background="Black"
CornerRadius="{Binding CornerRadius, RelativeSource={RelativeSource AncestorType=Border}}"
Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=Border}}"
Height="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType=Border}}"/>
</VisualBrush.Visual>
</VisualBrush>
</Border.OpacityMask>
<ItemsPresenter>
</ItemsPresenter>
</Border>
</ControlTemplate>
</ListView.Template>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Background="#01000000">
<Grid.ToolTip>
<StackPanel MinWidth="200">
<TextBlock Text="{Binding Name, Mode=OneWay}"
FontWeight="Bold"/>
<TextBlock Text="{Binding SelectedValue, Mode=OneWay}"/>
</StackPanel>
</Grid.ToolTip>
<TextBlock Text="{Binding Name}" >
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}" >
<Setter Property="Margin" Value="0"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Foreground" Value="{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType=ContentControl}}"/>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
3.数据模型定义与绑定
/// <summary>
/// ListViewsView.xaml 的交互逻辑
/// </summary>
public partial class ListViewsView : UserControl
{
public ListViewsView()
{
InitializeComponent();
// 获取数据源
MelphiDataSource = MelphiDataService.GetDataSource();
}
private ObservableCollection<MelphiDataItem> melphiDataSource = new ObservableCollection<MelphiDataItem>();
/// <summary>
/// 数据源
/// </summary>
public ObservableCollection<MelphiDataItem> MelphiDataSource
{
get
{
return melphiDataSource;
}
set
{
melphiDataSource = value;
}
}
}
/// <summary>
/// 数据服务
/// </summary>
public class MelphiDataService
{
/// <summary>
/// 模拟获取数据源
/// </summary>
/// <returns></returns>
public static ObservableCollection<MelphiDataItem> GetDataSource()
{
ObservableCollection<MelphiDataItem> melphiDatas = new ObservableCollection<MelphiDataItem>();
melphiDatas.Add(new MelphiDataItem()
{
Name = "DataA",
IsEnabled = true,
SelectedValue = "Kea",
SelectionSource = new List<string>() { "Kea", "Lau", "Nuh" }
});
melphiDatas.Add(new MelphiDataItem()
{
Name = "DataB",
IsEnabled = true,
SelectedValue = "Lau",
SelectionSource = new List<string>() { "Kea", "Lau", "Nuh" }
});
melphiDatas.Add(new MelphiDataItem()
{
Name = "DataC",
IsEnabled = true,
SelectedValue = "invalid",
SelectionSource = new List<string>() { "invalid", "valid" }
});
melphiDatas.Add(new MelphiDataItem()
{
Name = "DataD",
IsEnabled = true,
SelectedValue = "invalid",
SelectionSource = new List<string>() { "invalid", "valid" }
});
melphiDatas.Add(new MelphiDataItem()
{
Name = "DataE",
IsEnabled = false,
SelectedValue = "0",
SelectionSource = new List<string>() { "0", "1", "2", "3", "4", "5", "6" }
});
var listsource = new List<string>();
for (int i = 0; i <= 200; i+=20)
{
listsource.Add(i.ToString());
}
melphiDatas.Add(new MelphiDataItem()
{
Name = "DataF",
IsEnabled = true,
SelectedValue = "100",
SelectionSource = listsource
});
melphiDatas.Add(new MelphiDataItem()
{
Name = "DataG",
IsEnabled = true,
SelectedValue = "3",
SelectionSource = new List<string>() { "0", "1", "2", "3", "4", "5", "6" }
});
return melphiDatas;
}
}
/// <summary>
/// 数据项
/// </summary>
public class MelphiDataItem
{
/// <summary>
/// 数据名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 数据标识
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// 设定值
/// </summary>
public string SelectedValue { get; set; }
/// <summary>
/// 设定值选定项集合
/// </summary>
public List<string> SelectionSource { get; set; }
}
4.效果
ListBoxItem
对ListBox的项ListBoxItem而言,其定义和使用方式和ListViewItem几乎相同,其区别只是在于其类型为ListBoxItem。这里只展示ListBoxItem的定义,如下:
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="0" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="0" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Item项相关颜色 -->
<SolidColorBrush x:Key="Item.MouseOver.Background" Color="#DADADA"/>
<SolidColorBrush x:Key="Item.MouseOver.Border" Color="#3DDADADA"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Background" Color="#FF0B7AFF"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Border" Color="#3DDADADA"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Foreground" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="Item.SelectedActive.Background" Color="#FF0B7AFF"/>
<SolidColorBrush x:Key="Item.SelectedActive.Border" Color="#FF0B7AFF"/>
<SolidColorBrush x:Key="Item.SelectedActive.Foreground" Color="#FFFFFFFF"/>
<!-- Item项样式 -->
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Padding" Value="4,1"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentControl x:Name="Ct" Foreground="{TemplateBinding Foreground}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</ContentControl>
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.MouseOver.Border}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="False"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Border}"/>
<Setter Property="Foreground" TargetName="Ct" Value="{StaticResource Item.SelectedInactive.Foreground}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="True"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
<Setter Property="Foreground" TargetName="Ct" Value="{StaticResource Item.SelectedActive.Foreground}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
总结
在自定义控件样式,要学会使用VS提供的XAML模板编辑。其使用方法,在XAML的设计界面找到控件,然后右键–>编辑样式–>编辑副本。然后按照要求编辑生成一个默认的副本样式,认真学习并根据副本去设计自己样式。我相信,只要你肯努力和善于思考,你会变得更优秀。
Over
每次记录一小步…点点滴滴人生路…