WPF自定义控件06:FlatComboBox
在不少的应用程序中,经常需要使用下拉框(ComboBox)控件来从类别字段中选择,这样可以限定值的范围,让数据更加的规范,便于统计和分析。本文将介绍一下自定义的下拉框控件FlatComboBox,它与FlatCheckBox实现过程非常类似,它是继承自ComboBox,但使用自定义的UI样式来美化界面。下面将详细介绍具体的实现细节。
1 WPF项目结构
基于之前创建的WPF示例项目,在其中创建一个新的关于FlatComboBox的项目文件。本质上,FlatComboBox是继承ComboBox控件,利用ComboBox控件自身的属性和方法,可以减少FlatComboBox实现的难度和复杂度。首先,给出本项目文件结构,如下图所示:
其中的Fonts目录下存放各种图标字体文件,Style目录下存放各种控件的UI 样式定义文件,FlatComboBox.xaml就是FlatComboBox控件的样式定义文件。另外,需要将其注册到Generic.xaml文件中,示例代码如下:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Yd.WpfControls">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Yd.WpfControls;component/Style/IconFont.xaml"/>
<ResourceDictionary Source="/Yd.WpfControls;component/Style/FlatButton.xaml"/>
<ResourceDictionary Source="/Yd.WpfControls;component/Style/FlatCheckBox.xaml"/>
<ResourceDictionary Source="/Yd.WpfControls;component/Style/FlatRadioButton.xaml"/>
<ResourceDictionary Source="/Yd.WpfControls;component/Style/ToggleButton.xaml"/>
<ResourceDictionary Source="/Yd.WpfControls;component/Style/FlatComboBox.xaml"/>
</ResourceDictionary.MergedDictionaries>
2 WPF FlatComboBox实现
首先在Yd.WpfControls项目中添加一个类FlatComboBox.cs,它继承自ComboBox控件,示例代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Yd.WpfControls
{
public class FlatComboBox : ComboBox
{
static FlatComboBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(FlatComboBox),
new FrameworkPropertyMetadata(typeof(FlatComboBox)));
}
}
}
FlatComboBox控件的UI样式主要就是依靠FlatComboBox.xaml文件进行定义的,示例代码如下:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Yd.WpfControls">
<!--图标字体引入-->
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Yd.WpfControls;component/Style/IconFont.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!-- Flat ComboBox -->
<SolidColorBrush x:Key="ComboBoxNormalBorderBrush" Color="#e3e9ef" />
<SolidColorBrush x:Key="ComboBoxNormalBackgroundBrush" Color="#fff" />
<SolidColorBrush x:Key="ComboBoxDisabledForegroundBrush" Color="#999" />
<SolidColorBrush x:Key="ComboBoxDisabledBorderBrush" Color="#999" />
<SolidColorBrush x:Key="ComboBoxDisabledBackgroundBrush" Color="#eee" />
<Style x:Key="{x:Type ComboBoxItem}" TargetType="{x:Type ComboBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Border x:Name="Border"
Padding="1" SnapsToDevicePixels="True" Background="Transparent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected" />
<VisualState x:Name="Selected">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0"
Value="#BDC3C7" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="SelectedUnfocused">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Panel.Background). (SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0"
Value="#e3e9ef" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate TargetType="ToggleButton" x:Key="ComboBoxToggleButtonTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="26" />
</Grid.ColumnDefinitions>
<Border Grid.ColumnSpan="2" Name="Border"
BorderBrush="#16a085"
CornerRadius="0" BorderThickness="1, 1, 1, 1"
Background="#16a085" />
<Border Grid.Column="1" Margin="1, 1, 1, 1" BorderBrush="#16a085" Name="ButtonBorder"
CornerRadius="0, 0, 0, 0" BorderThickness="0, 0, 0, 0"
Background="#16a085" />
<TextBlock Name="Arrow" Grid.Column="1" Text=""
Style="{StaticResource faFont}" SnapsToDevicePixels="True"
FontSize="20" HorizontalAlignment="Left" VerticalAlignment="Center"
Margin="3,3,3,3" Background="Transparent"
Foreground="White"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsMouseOver" Value="True">
<Setter Property="Panel.Background" TargetName="ButtonBorder" Value="#16a085"/>
<Setter Property="Shape.Fill" TargetName="Arrow" Value="Transparent"/>
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter Property="Panel.Background" TargetName="ButtonBorder" Value="#16a085"/>
<Setter Property="Shape.Fill" TargetName="Arrow" Value="Transparent"/>
</Trigger>
<Trigger Property="UIElement.IsEnabled" Value="False">
<Setter Property="Panel.Background" TargetName="Border" Value="{StaticResource ComboBoxDisabledBackgroundBrush}"/>
<Setter Property="Panel.Background" TargetName="ButtonBorder" Value="{StaticResource ComboBoxDisabledBackgroundBrush}"/>
<Setter Property="Border.BorderBrush" TargetName="ButtonBorder" Value="{StaticResource ComboBoxDisabledBorderBrush}"/>
<Setter Property="TextElement.Foreground" Value="{StaticResource ComboBoxDisabledForegroundBrush}"/>
<Setter Property="Shape.Fill" TargetName="Arrow" Value="Transparent"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="ComboBoxFlatStyle" TargetType="{x:Type ComboBox}">
<Setter Property="UIElement.SnapsToDevicePixels" Value="True"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
<Setter Property="TextElement.Foreground" Value="White"/>
<Setter Property="FrameworkElement.FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<ToggleButton Name="ToggleButton" Grid.Column="2"
ClickMode="Press" Focusable="False"
IsChecked="{Binding Path=IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
Template="{StaticResource ComboBoxToggleButtonTemplate}"/>
<ContentPresenter Name="ContentSite" Margin="5, 3, 23, 3" IsHitTestVisible="False"
HorizontalAlignment="Left" VerticalAlignment="Center"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding ComboBox.SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"/>
<TextBox Name="PART_EditableTextBox" Margin="3, 3, 23, 3"
IsReadOnly="{TemplateBinding IsReadOnly}"
Visibility="Hidden" Background="Transparent"
HorizontalAlignment="Left" VerticalAlignment="Center" Foreground="{x:Static local:FlatColors.CLOUDS}"
Focusable="True" >
<TextBox.Template>
<ControlTemplate TargetType="TextBox" >
<Border Name="PART_ContentHost" Focusable="False" />
</ControlTemplate>
</TextBox.Template>
</TextBox>
<!-- Popup showing items -->
<Popup Name="Popup" Placement="Bottom"
Focusable="False" AllowsTransparency="True"
IsOpen="{TemplateBinding ComboBox.IsDropDownOpen}"
PopupAnimation="Slide">
<Grid Name="DropDown" SnapsToDevicePixels="True"
MinWidth="{TemplateBinding FrameworkElement.ActualWidth}"
MaxHeight="{TemplateBinding ComboBox.MaxDropDownHeight}">
<Border Name="DropDownBorder" Background="#ECF0F1" Margin="0, 1, 0, 0"
CornerRadius="0" BorderThickness="1,1,1,1"
BorderBrush="#ECF0F1"/>
<ScrollViewer Margin="1" SnapsToDevicePixels="True" Foreground="Black" FocusVisualStyle="{x:Null}">
<ItemsPresenter KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ItemsControl.HasItems" Value="False">
<Setter Property="FrameworkElement.MinHeight" TargetName="DropDownBorder" Value="95"/>
</Trigger>
<Trigger Property="UIElement.IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" Value="{StaticResource ComboBoxDisabledForegroundBrush}"/>
</Trigger>
<Trigger Property="ItemsControl.IsGrouping" Value="True">
<Setter Property="ScrollViewer.CanContentScroll" Value="False"/>
</Trigger>
<Trigger Property="ComboBox.IsEditable" Value="True">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False"/>
<Setter Property="UIElement.Visibility" TargetName="PART_EditableTextBox" Value="Visible"/>
<Setter Property="UIElement.Visibility" TargetName="ContentSite" Value="Hidden"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:FlatComboBox}" BasedOn="{StaticResource ComboBoxFlatStyle}" >
<Setter Property="FontSize" Value="{x:Static local:FlatFonts.contentFontSize}"></Setter>
<Setter Property="Foreground" Value="White"></Setter>
</Style>
</ResourceDictionary>
其中 <Style TargetType="{x:Type local:FlatComboBox}" BasedOn="{StaticResource ComboBoxFlatStyle}" > 语句中的BasedOn表示继承关系,即一个新的Style可以通过BasedOn来继承,这样就可以更加方便的管理样式。另外,下拉框中的小三角形状是通过图标字体实现的,<TextBlock Name="Arrow" Grid.Column="1" Text="" Style="{StaticResource faFont}" ...> 。
3 WPF FlatComboBox测试
首先,需要重新生成一下项目文件,然后在WpfControls项目中添加自定义控件FlatComboBox,MainWindow.xaml部分示例代码如下:
<WpfControls:FlatComboBox HorizontalAlignment="Left" Height="30"
x:Name="ccb"
Margin="424,181,0,0" VerticalAlignment="Top" Width="255"/>
其中的x:Name="ccb"给控件起一个名称,这样可以在后台用C#来进行访问,下面给出后台初始化数据的示例代码:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<String> list = new List<string>();
list.Add("国学");
list.Add("高数");
list.Add("绘画");
this.ccb.ItemsSource = list;
}
}
运行界面如下:
- 点赞
- 收藏
- 关注作者
评论(0)