WPF 自定义ComboBox样式,自定义多选控件
引用网址:https://www.cnblogs.com/xiaomingg/p/8748286.html
一、ComboBox基本样式
ComboBox有两种状态,可编辑和不可编辑状态。通过设置IsEditable属性可以切换控件状态。
先看基本样式效果:
基本样式代码如下:
<!--ComboBox--> <!--ComBoBox项选中背景色--> <SolidColorBrush x:Key="ComboBoxSelectdBackground" Color="#ff8c69"/> <!--ComBoBox项鼠标经过背景色--> <SolidColorBrush x:Key="ComboBoxMouseOverBackground" Color="#ff3030"/> <!--ComBoBox项选中前景色--> <SolidColorBrush x:Key="ComboBoxSelectedForeground" Color="White"/> <!--ComBoBox项鼠标经过前景色--> <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 已数据绑定到 ComboBox 本身以切换 IsDropDownOpen--> <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 命名为 PART_EditableTextBox,否则 ComboBox 将无法识别它--> <TextBox Visibility="Hidden" BorderThickness="0" Margin="2 0 0 0" x:Name="PART_EditableTextBox" VerticalAlignment="Center" Focusable="True" Background="Transparent" IsReadOnly="{TemplateBinding IsReadOnly}"/> <!--Popup 可显示 ComboBox 中的项列表。IsOpen 已数据绑定到通过 ComboBoxToggleButton 来切换的 IsDropDownOpen--> <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--> <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); } /// <summary> /// 选中项列表 /// </summary> public ObservableCollection<MultiCbxBaseData> ChekedItems { get { return (ObservableCollection<MultiCbxBaseData>)GetValue(ChekedItemsProperty); } set { SetValue(ChekedItemsProperty, value); } } public static readonly DependencyProperty ChekedItemsProperty = DependencyProperty.Register("ChekedItems", typeof(ObservableCollection<MultiCbxBaseData>), typeof(MultiComboBox), new PropertyMetadata(new ObservableCollection<MultiCbxBaseData>(), OnPropertyChanged)); /// <summary> /// ListBox竖向列表 /// </summary> private ListBox _ListBoxV; /// <summary> /// ListBox横向列表 /// </summary> 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; /// <summary> /// 关联主键 /// </summary> public int ID { get { return _id; } set { _id = value; } } private string _viewName; /// <summary> /// 显示名称 /// </summary> public string ViewName { get { return _viewName; } set { _viewName = value; } } private bool _isCheck; /// <summary> /// 是否选中 /// </summary> 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> <!--MultiComboBox普通样式--> <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<MultiCbxBaseData>() { 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<MultiCbxBaseData> MultiComboBoxList; }
所有代码已经上传到github:https://github.com/cmfGit/WpfDemo.git
作者:一叶知秋