文章目录
1.功能
1.1 实现功能
先看效果:
- 1、对其中鼠标悬浮、选中的样式进行统一
- 2、对其中滚动条样式进行全局统一配置
- 3、对控件的Border Color 进行全局统一配置
- 4、绑定数据时设置已选中项
- 5、Popup 宽度根据内容自适应
- 6、MutliCombobox可设置已选中项展示样式(横向/纵向)
1.2 全局配置
<!--默认Border Color-->
<SolidColorBrush x:Key="DefaultBrush" Color="#eaeaea"></SolidColorBrush>
<!--默认全局颜色-->
<SolidColorBrush x:Key="DefaultBackground" Color="#1ab394"></SolidColorBrush>
<!--鼠标悬浮背景颜色-->
<SolidColorBrush x:Key="MouseOverBackground" Color="#46e6c6"></SolidColorBrush>
<!--鼠标选中背景颜色-->
<SolidColorBrush x:Key="SelectedBackground" Color="#1ab394"></SolidColorBrush>
2. Combobox
2.1 Style
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="BorderBrush" Value="{StaticResource DefaultBrush}"></Setter>
<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"/>
<ContentPresenter ContentSource="{Binding Source}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="3 0 3 0"></ContentPresenter>
<Border x:Name="_border" Background="White" Opacity="0"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="_borderbg" Property="Background" Value="{StaticResource SelectedBackground}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="false"/>
<Condition Property="IsMouseOver" Value="true"/>
</MultiTrigger.Conditions>
<Setter TargetName="_borderbg" Property="Background" Value="{StaticResource MouseOverBackground}" />
</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" x:Name="ToggleButton"
Template="{StaticResource ComboBoxToggleButton}" 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="{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}" x:Name="DropDown" SnapsToDevicePixels="True">
<Border x:Name="DropDownBorder" BorderBrush="#e8e8e8" BorderThickness="1 0 1 1"/>
<ScrollViewer Padding="0 0 12 0" 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.Style>
3. MutliCombobox
3.1 控件代码
3.1.1 Style
<ComboBox.Style>
<!--MultiComboBox普通样式-->
<Style TargetType="{x:Type ComboBox}">
<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 ComboBox}">
<Grid>
<Border x:Name="Bg" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
BorderBrush="{StaticResource DefaultBrush}" BorderThickness="1" >
<Grid>
<Grid x:Name="PART_InnerGrid" Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="0.3*" MaxWidth="30" />
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding CheckedItems,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
SelectionMode="Multiple" BorderThickness="0"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="{Binding Path=CheckedOrientation, Mode=TwoWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ComboBox}}" VirtualizingStackPanel.IsVirtualizing="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Left"/>
<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 Text}"
IsChecked="true"
Click="CheckBox_Click"
/>
</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 可显示 ComboBox 中的项列表。IsOpen 已数据绑定到通过 ComboBoxToggleButton 来切换的 IsDropDownOpen-->
<Popup IsOpen="{TemplateBinding IsDropDownOpen}" Placement="Bottom"
x:Name="Popup" Focusable="False" AllowsTransparency="True" PopupAnimation="Slide">
<Grid MaxHeight="150" MinWidth="{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}"
x:Name="DropDown" SnapsToDevicePixels="True">
<ListBox SelectionMode="Multiple" BorderThickness="1 0 1 1" Background="White" Padding="0 0 12 0"
ItemsSource="{Binding MultiItemSource,RelativeSource={RelativeSource TemplatedParent}}"
MaxHeight="{TemplateBinding MaxDropDownHeight}" BorderBrush="{StaticResource DefaultBrush}"
SelectionChanged="ListBoxItems_SelectionChanged">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}" >
<Grid Height="22">
<Border x:Name="bg" BorderBrush="{StaticResource DefaultBrush}" 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="{StaticResource SelectedBackground}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsSelected" Value="False"/>
</MultiTrigger.Conditions>
<Setter TargetName="bg" Property="Background" Value="{StaticResource MouseOverBackground}" />
<Setter TargetName="bg" Property="Opacity" Value="0.7"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="bg" Property="Opacity" Value="0.3" />
<Setter Property="Foreground" Value="Gray" />
</Trigger>
<DataTrigger Binding="{Binding IsChecked}" Value="True">
<Setter Property="IsSelected" Value="True"></Setter>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<CheckBox x:Name="chk" VerticalAlignment="Center"
Foreground="{Binding Foreground,RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
IsChecked="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem},Path=IsSelected,Mode=TwoWay}"
Content="{Binding Path=Text}"/>
</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>
</ComboBox.Style>
3.1.2 后台代码
/// <summary>
/// MultiComboBox.xaml 的交互逻辑
/// </summary>
public partial class MultiComboBox : System.Windows.Controls.ComboBox, INotifyPropertyChanged
{
public MultiComboBox()
{
InitializeComponent();
}
#region Fields
/// <summary>
/// 选中项列表
/// </summary>
private ObservableCollection<MultiComboBoxItem> CheckedItemsProperty = new ObservableCollection<MultiComboBoxItem>();
[Bindable(true)]
public ObservableCollection<MultiComboBoxItem> CheckedItems
{
get { return CheckedItemsProperty; }
set
{
CheckedItemsProperty = value;
RaisePropertyChanged("CheckedItems");
}
}
/// <summary>
/// 展示列表
/// </summary>
private ObservableCollection<MultiComboBoxItem> MultiItemSourceProperty = new ObservableCollection<MultiComboBoxItem>();
[Bindable(true)]
public ObservableCollection<MultiComboBoxItem> MultiItemSource
{
get { return MultiItemSourceProperty; }
set
{
MultiItemSourceProperty = value;
RaisePropertyChanged("MultiItemSource");
}
}
[Bindable(true)]
public Orientation CheckedOrientation
{
get { return (Orientation)GetValue(OrientationProperty); }
set
{
SetValue(OrientationProperty, value);
}
}
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("CheckedOrientation",
typeof(Orientation),
typeof(MultiComboBox),
new PropertyMetadata(Orientation.Horizontal));
#endregion
#region Method
/// <summary>
/// 设置数据源
/// </summary>
/// <param name="source"></param>
public void SetSource(IEnumerable<MultiComboBoxItem> source)
{
MultiItemSource = new ObservableCollection<MultiComboBoxItem>(source);
if (MultiItemSource.Any(x => x.IsChecked))
CheckedItems = new ObservableCollection<MultiComboBoxItem>(source.Where(x => x.IsChecked).ToList());
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region Event
/// <summary>
/// 下拉列表选中变更
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ListBoxItems_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (var item in e.AddedItems)
{
MultiComboBoxItem datachk = item as MultiComboBoxItem;
datachk.IsChecked = true;
if (CheckedItems.IndexOf(datachk) < 0)
CheckedItems.Add(datachk);
}
foreach (var item in e.RemovedItems)
{
MultiComboBoxItem datachk = item as MultiComboBoxItem;
datachk.IsChecked = false;
CheckedItems.Remove(datachk);
}
}
/// <summary>
/// 复选框取消选中
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
var ckb = (CheckBox)sender;
ListBoxItem cp = ckb.TemplatedParent as ListBoxItem;
MultiComboBoxItem item = cp.Content as MultiComboBoxItem;
item.IsChecked = false;
CheckedItems.Remove(item);
}
#endregion
}
3.2 调用方法
- XAML
<fcode:MultiComboBox x:Name="mtcbbTest" ></fcode:MultiComboBox>
- 后台代码
//设置数据源
mtcbbTest.SetSource(source);
//获取已选中项
mtcbbTest.CheckedItems;
4 代码资源下载
本文代码资源已上传:https://download.csdn.net/download/johnson55925/10800128
项目开源地址:
https://gitee.com/fcode_me/FCODE_DEMO