WPF 自定义ComboBox样式,自定义多选控件
一、ComboBox基本样式
ComboBox有两种状态,可编辑和不可编辑状态。通过设置IsEditable属性可以切换控件状态。
先看基本样式效果:
基本样式代码如下:
<SolidColorBrush x:Key="ComboBoxSelectdBackground" Color="#ff8c69"/> <SolidColorBrush x:Key="ComboBoxMouseOverBackground" Color="#ff3030"/> <SolidColorBrush x:Key="ComboBoxSelectedForeground" Color="White"/> <SolidColorBrush x:Key="ComboBoxMouseOverForegrond" Color="White"/> <Style TargetType="{x:Type ComboBox}"> <Setter Property="ItemContainerStyle"> <Setter.Value> <Style TargetType="ComboBoxItem"> <Setter Property="Height" Value="20"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ComboBoxItem}"> <Grid Height="{TemplateBinding Height}" Width="{TemplateBinding Width}"> <Border x:Name="_borderbg" Background="Transparent"/> <TextBlock Margin="3 0 3 0" VerticalAlignment="Center" x:Name="_txt" Foreground="#333" Text="{Binding Content,RelativeSource={RelativeSource TemplatedParent}}"/> <Border x:Name="_border" Background="White" Opacity="0"/> Grid> <ControlTemplate.Triggers> <Trigger Property="IsSelected" Value="true"> <Setter TargetName="_borderbg" Property="Background" Value="{StaticResource ComboBoxSelectdBackground}" /> <Setter TargetName="_txt" Property="Foreground" Value="{StaticResource ComboBoxSelectedForeground}"/> Trigger> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="IsSelected" Value="false"/> <Condition Property="IsMouseOver" Value="true"/> MultiTrigger.Conditions> <Setter TargetName="_borderbg" Property="Background" Value="{StaticResource ComboBoxMouseOverBackground}" /> <Setter TargetName="_txt" Property="Foreground" Value="{StaticResource ComboBoxMouseOverForegrond}"/> MultiTrigger> ControlTemplate.Triggers> ControlTemplate> Setter.Value> Setter> Style> Setter.Value> Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ComboBox}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.7*"/> <ColumnDefinition Width="0.3*" MaxWidth="30"/> Grid.ColumnDefinitions> <Border Grid.Column="0" Grid.ColumnSpan="2" BorderThickness="1" BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="1,0,0,1"/> <ContentPresenter HorizontalAlignment="Left" Margin="3,3,0,3" x:Name="ContentSite" VerticalAlignment="Center" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" IsHitTestVisible="False"/> <ToggleButton Grid.Column="0" Grid.ColumnSpan="2" Template="{StaticResource ComboBoxToggleButton}" x:Name="ToggleButton" Focusable="false" IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press"/> <TextBox Visibility="Hidden" BorderThickness="0" Margin="2 0 0 0" x:Name="PART_EditableTextBox" VerticalAlignment="Center" Focusable="True" Background="Transparent" IsReadOnly="{TemplateBinding IsReadOnly}"/> <Popup IsOpen="{TemplateBinding IsDropDownOpen}" Placement="Bottom" x:Name="Popup" Focusable="False" AllowsTransparency="True" PopupAnimation="Slide"> <Grid MaxHeight="150" MinWidth="{TemplateBinding ActualWidth}" x:Name="DropDown" SnapsToDevicePixels="True"> <Border x:Name="DropDownBorder" BorderBrush="#e8e8e8" BorderThickness="1 0 1 1"/> <ScrollViewer Margin="1" SnapsToDevicePixels="True" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" CanContentScroll="True"> <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" Background="White"/> ScrollViewer> Grid> Popup> Grid> <ControlTemplate.Triggers> <Trigger Property="IsEditable" Value="true"> <Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible" /> Trigger> ControlTemplate.Triggers> ControlTemplate> Setter.Value> Setter> Style>
引用示例:
<ComboBox x:Name="combobox" Width="150" Margin="10" IsEditable="False" BorderBrush="#e8e8e8"> <CheckBox Content="上海" Tag="1">CheckBox> <CheckBox Content="北京" Tag="2">CheckBox> <CheckBox Content="天津" Tag="3">CheckBox> <CheckBox Content="广州" Tag="4">CheckBox> ComboBox>
二、ComboBox扩展样式(多选控件)
ComBoBox能够单选选择数据,那么能不能实现多选的操作呢,答案是肯定的。这里多选的自定义控件参考了博主“梦里花落知多少”的内容。我对样式做了补充,使其能够更方便的进行移除多选的内容。同时也更好的展示了已选的内容,大家可以根据实际需求做出更好的展示效果。
先看效果:
2.1、添加自定义控件MultiComboBox
public class MultiComboBox : ComboBox { static MultiComboBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(MultiComboBox), new FrameworkPropertyMetadata(typeof(MultiComboBox))); } private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { d.SetValue(e.Property, e.NewValue); } ////// 选中项列表 /// public ObservableCollectionChekedItems { get { return (ObservableCollection )GetValue(ChekedItemsProperty); } set { SetValue(ChekedItemsProperty, value); } } public static readonly DependencyProperty ChekedItemsProperty = DependencyProperty.Register("ChekedItems", typeof(ObservableCollection ), typeof(MultiComboBox), new PropertyMetadata(new ObservableCollection (), OnPropertyChanged)); /// /// ListBox竖向列表 /// private ListBox _ListBoxV; ////// ListBox横向列表 /// private ListBox _ListBoxH; public override void OnApplyTemplate() { base.OnApplyTemplate(); _ListBoxV = Template.FindName("PART_ListBox", this) as ListBox; _ListBoxH = Template.FindName("PART_ListBoxChk", this) as ListBox; _ListBoxH.ItemsSource = ChekedItems; _ListBoxV.SelectionChanged += _ListBoxV_SelectionChanged; _ListBoxH.SelectionChanged += _ListBoxH_SelectionChanged; if (ItemsSource != null) { foreach (var item in ItemsSource) { MultiCbxBaseData bdc = item as MultiCbxBaseData; if (bdc.IsCheck) { _ListBoxV.SelectedItems.Add(bdc); } } } } private void _ListBoxH_SelectionChanged(object sender, SelectionChangedEventArgs e) { foreach (var item in e.RemovedItems) { MultiCbxBaseData datachk = item as MultiCbxBaseData; for (int i = 0; i < _ListBoxV.SelectedItems.Count; i++) { MultiCbxBaseData datachklist = _ListBoxV.SelectedItems[i] as MultiCbxBaseData; if (datachklist.ID == datachk.ID) { _ListBoxV.SelectedItems.Remove(_ListBoxV.SelectedItems[i]); } } } } void _ListBoxV_SelectionChanged(object sender, SelectionChangedEventArgs e) { foreach (var item in e.AddedItems) { MultiCbxBaseData datachk = item as MultiCbxBaseData; datachk.IsCheck = true; if (ChekedItems.IndexOf(datachk) < 0) { ChekedItems.Add(datachk); } } foreach (var item in e.RemovedItems) { MultiCbxBaseData datachk = item as MultiCbxBaseData; datachk.IsCheck = false; ChekedItems.Remove(datachk); } } public class MultiCbxBaseData { private int _id; ////// 关联主键 /// public int ID { get { return _id; } set { _id = value; } } private string _viewName; ////// 显示名称 /// public string ViewName { get { return _viewName; } set { _viewName = value; } } private bool _isCheck; ////// 是否选中 /// public bool IsCheck { get { return _isCheck; } set { _isCheck = value;} } } }
2.2、定义MultiComboBox控件的样式
<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}"> <Grid Height="25"> <Border Grid.Column="1" Background="White" Opacity="0" Cursor="Hand"/> <Path x:Name="Arrow" Grid.Column="1" Data="M 0 0 6 6 12 0 Z" VerticalAlignment="Center" HorizontalAlignment="Center" Stretch="None" Fill="#B1B1B1" /> Grid> <ControlTemplate.Triggers> <Trigger Property="IsChecked" Value="true"> <Setter TargetName="Arrow" Property="RenderTransform"> <Setter.Value> <RotateTransform CenterX="6" CenterY="3" Angle="180">RotateTransform> Setter.Value> Setter> <Setter TargetName="Arrow" Property="Margin" Value="0 0 0 2"/> Trigger> ControlTemplate.Triggers> ControlTemplate> <Style TargetType="{x:Type local:MultiComboBox}"> <Setter Property="Width" Value="200" /> <Setter Property="HorizontalContentAlignment" Value="Stretch" /> <Setter Property="VerticalContentAlignment" Value="Center" /> <Setter Property="SnapsToDevicePixels" Value="True" /> <Setter Property="MaxDropDownHeight" Value="400" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:MultiComboBox}"> <Grid> <Border x:Name="Bg" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" BorderBrush="#eaeaea" BorderThickness="1" > <Grid x:Name="PART_Root"> <Grid x:Name="PART_InnerGrid" Margin="0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="0.3*" MaxWidth="30" /> Grid.ColumnDefinitions> <ListBox x:Name="PART_ListBoxChk" SelectionMode="Multiple" BorderThickness="0" ScrollViewer.VerticalScrollBarVisibility="Disabled"> <ListBox.ItemsPanel> <ItemsPanelTemplate> <VirtualizingStackPanel Orientation="Horizontal" VirtualizingStackPanel.IsVirtualizing="True" /> ItemsPanelTemplate> ListBox.ItemsPanel> <ListBox.ItemContainerStyle> <Style TargetType="ListBoxItem"> <Setter Property="BorderThickness" Value="0"/> <Setter Property="IsSelected" Value="True"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ListBoxItem"> <CheckBox BorderThickness="0" VerticalAlignment="Center" HorizontalAlignment="Center" Content="{Binding ViewName}" IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/> ControlTemplate> Setter.Value> Setter> Style> ListBox.ItemContainerStyle> ListBox> <ToggleButton x:Name="PART_DropDownToggle" IsTabStop="False" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Grid.Column="1" Template="{StaticResource ComboBoxToggleButton}" /> Grid> Grid> Border> <Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" StaysOpen="False" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom"> <Grid Width="{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}" MaxHeight="{Binding MaxDropDownHeight, RelativeSource={RelativeSource TemplatedParent}}" > <ListBox x:Name="PART_ListBox" SelectionMode="Multiple" BorderThickness="1 0 1 1" Background="White" ItemsSource="{Binding ItemsSource,RelativeSource={RelativeSource TemplatedParent}}" MaxHeight="{TemplateBinding MaxDropDownHeight}" BorderBrush="#eaeaea" > <ListBox.ItemContainerStyle> <Style TargetType="ListBoxItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBoxItem}" > <Grid Height="22"> <Border x:Name="bg" BorderBrush="#eaeaea" BorderThickness="0"/> <ContentPresenter x:Name="content" /> <Border Background="White" Opacity="0"/> Grid> <ControlTemplate.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter TargetName="bg" Property="Background" Value="#ADD6FF" /> Trigger> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="IsMouseOver" Value="true" /> <Condition Property="IsSelected" Value="false"/> MultiTrigger.Conditions> <Setter TargetName="bg" Property="Background" Value="#009BDB" /> <Setter TargetName="bg" Property="Opacity" Value="0.7"/> <Setter Property="Foreground" Value="White" /> MultiTrigger> <Trigger Property="IsEnabled" Value="False"> <Setter TargetName="bg" Property="Opacity" Value="0.3" /> <Setter Property="Foreground" Value="Gray" /> Trigger> ControlTemplate.Triggers> ControlTemplate> Setter.Value> Setter> Style> ListBox.ItemContainerStyle> <ListBox.ItemTemplate> <DataTemplate> <Grid> <CheckBox x:Name="chk" Visibility="Hidden" IsChecked="{Binding IsCheck,Mode=TwoWay}" VerticalAlignment="Center"/> <CheckBox VerticalAlignment="Center" Foreground="{Binding Foreground,RelativeSource={RelativeSource AncestorType=ListBoxItem}}" IsChecked="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem},Path=IsSelected,Mode=TwoWay}" Content="{Binding Path=ViewName}" /> Grid> <DataTemplate.Triggers> <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem},Path=IsSelected}" Value="true"> <Setter TargetName="chk" Property="IsChecked" Value="true"/> DataTrigger> <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem},Path=IsSelected}" Value="false"> <Setter TargetName="chk" Property="IsChecked" Value="false"/> DataTrigger> DataTemplate.Triggers> DataTemplate> ListBox.ItemTemplate> ListBox> Grid> Popup> Grid> ControlTemplate> Setter.Value> Setter> Style>
2.3、引用示例:
<local:MultiComboBox x:Name="multiCmb" Margin="10" Width="200"/>
2.4、后台代码(初始化绑定数据):
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); MultiComboBoxList = new ObservableCollection() { new MultiCbxBaseData(){ ID=0, ViewName="张三", IsCheck=false }, new MultiCbxBaseData(){ ID=1, ViewName="李四", IsCheck=false }, new MultiCbxBaseData(){ ID=2, ViewName="王五", IsCheck=false }, new MultiCbxBaseData(){ ID=3, ViewName="马六", IsCheck=false }, new MultiCbxBaseData(){ ID=4, ViewName="赵七", IsCheck=false }, }; this.multiCmb.ItemsSource = MultiComboBoxList; } private ObservableCollection MultiComboBoxList; }
所有代码已经上传到github:https://github.com/cmfGit/WpfDemo.git