WPF涉及到很多自定义样式的实现,以下是我实现的ComboBox+TabControl控件相结合的自定义样式,实现效果如下:
实现步骤:
(1)考虑到可以在多个地方使用该控件,创建一个通用的UserControl来实现该控件;
<UserControl x:Class="Teacher.UserControls.Common.ComboxTabControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Teacher.UserControls.Common"
mc:Ignorable="d" DataContextChanged="UserControl_DataContextChanged" >
<Grid>
<ComboBox Name="ComboxBtn" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderBrush="#EBEFF5" Background="#f9fafc" ItemsSource="{Binding Path=ComboxList}" DropDownClosed="ComboxBtn_DropDownClosed" SelectedIndex="0" >
</ComboBox>
</Grid>
</UserControl>
属性说明如下:
(2)重写ComboBox的样式
<UserControl.Resources>
<Style x:Key="{x:Type ComboBox}" TargetType="ComboBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<ToggleButton FontSize="{TemplateBinding FontSize}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" MinWidth="60" MinHeight="30" Foreground="#666666" Content="{Binding DisplayVaule}" Style="{StaticResource ComboBoxToggleButton}" x:Name="ToggleButton" Focusable="false" IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press"/>
<Popup IsOpen="{TemplateBinding IsDropDownOpen}" Placement="Bottom" x:Name="Popup" Focusable="False" AllowsTransparency="True" PopupAnimation="Slide">
<Border Name="DropDown" SnapsToDevicePixels="True"
Width="{TemplateBinding ActualWidth}" MaxHeight="300">
<Border.Effect>
<DropShadowEffect BlurRadius="14" ShadowDepth="4.5" Direction="270" Color="#000000" Opacity=".1" RenderingBias="Performance"/>
</Border.Effect>
<TabControl Name="SelfTabControl" Style="{StaticResource TabControlStyle}">
<!--TabControl内容后面会补充说明 -->
</TabControl>
</Border>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
属性说明如下:
通过自定义的Style来实现特殊样式
下拉列表按钮开关,点击时要呼出下拉选项;
其中Content处的内容绑定的是最终要展现的值;Style处是ToggleButton自定义风格样式;
IsChecked则和Popup里的IsOpen的值双向绑定,在点击时,呼出Popup,再次点击则关闭Popup;
ToggleButton风格实现
<Style x:Key="ComboBoxToggleButton" TargetType="ToggleButton" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border Background="{TemplateBinding Background}" BorderThickness="1" BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="2">
<Grid Width="{Binding}" VerticalAlignment="Center" Cursor="Hand" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="1" HorizontalAlignment="Left" TextBlock.FontSize="{TemplateBinding FontSize}" TextBlock.Foreground="{TemplateBinding Foreground}" VerticalAlignment="Center" ContentSource="{Binding}" />
<Image HorizontalAlignment="Center" Grid.Column="2" Width="12" Height="12" Source="/Resource/image/exam/三角箭头上.png" Name="icon" Cursor="Hand" RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<RotateTransform x:Name="rotateTransform" />
</Image.RenderTransform>
</Image>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="False" >
<Setter Property="LayoutTransform" TargetName="icon">
<Setter.Value>
<RotateTransform Angle="180"/>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
属性说明:
(3)接下来是重头戏:TabControl的自定义样式实现
<TabControl Name="SelfTabControl" Style="{StaticResource TabControlStyle}">
<!--TabItem内容后面会补充说明 -->
</TabControl>
TabControlStyle实现如下:
<Style x:Key="TabControlStyle" TargetType="{x:Type TabControl}">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="#FFECECEC"/>
<Setter Property="BorderThickness" Value="1,0,1,1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid x:Name="templateRoot" ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="ColumnDefinition0"/>
<ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="RowDefinition0" Height="Auto"/>
<RowDefinition x:Name="RowDefinition1" Height="*"/>
</Grid.RowDefinitions>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,0,1,0" >
<UniformGrid x:Name="headerPanel" Rows="1" Background="Transparent" Grid.Column="0" IsItemsHost="true" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1"/>
</Border>
<Border x:Name="contentPanel" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local">
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="0" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
属性说明:
(4)依然是实现重点:TabControl下增加两个TabItem项,Header分别是“标志”、“处理”,因“标志”、“处理”两处的TabItem实现除数据源不同外,其他基本一致,所以后面以“标志”项作为例子讲解;
<TabControl Name="SelfTabControl" Style="{StaticResource TabControlStyle}">
<TabItem Header="标志" IsSelected="{Binding Path=ShowFlagTab}" Style="{StaticResource TabItemStyle}">
<ItemsControl Width="{TemplateBinding ActualWidth}" ItemsSource="{Binding Path=FlagList}" HorizontalAlignment="Left" BorderThickness="0" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<MenuItem x:Name="btnFlag" Height="34" Background="Transparent" VerticalAlignment="Center" IsCheckable="True" IsChecked="{Binding IsChecked}" Click="BtnFlag_Click" >
<MenuItem.Template>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Border x:Name="border" Width="{TemplateBinding ActualWidth}" Height="34" Background="Transparent" BorderBrush="#FFECECEC" CornerRadius="0" BorderThickness="0" SnapsToDevicePixels="True" VerticalAlignment="Center">
<WrapPanel VerticalAlignment="Center">
<RadioButton Visibility="Collapsed" x:Name="rdo" IsChecked="{Binding IsChecked,RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}" GroupName="A" IsThreeState="False"/>
<TextBlock x:Name="txt" Margin="20,0" Foreground="#5A646E" FontSize="14" Text="{Binding Path=Name}" VerticalAlignment="Center" ></TextBlock>
</WrapPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="border" Property="Background" Value="#F4F6FD"></Setter>
</Trigger>
<Trigger Property="IsChecked" SourceName="rdo" Value="true">
<Setter TargetName="txt" Property="Foreground" Value="#00C18A"></Setter>
<Setter Property="IsChecked" Value="true"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</MenuItem.Template>
</MenuItem>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</TabItem>
<TabItem Header="处理" IsSelected="{Binding Path=ShowHandleTab}" Style="{StaticResource TabItemStyle}">
<ItemsControl Width="{TemplateBinding ActualWidth}" ItemsSource="{Binding Path=HandleList}" HorizontalAlignment="Left" BorderThickness="0" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<MenuItem x:Name="btnHandle" Height="34" Background="Transparent" VerticalAlignment="Center" IsCheckable="True" IsChecked="{Binding IsChecked}" Click="BtnHandle_Click" >
<MenuItem.Template>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Border x:Name="border" Width="{TemplateBinding ActualWidth}" Height="34" Background="Transparent" BorderBrush="#FFECECEC" CornerRadius="0" BorderThickness="0" SnapsToDevicePixels="True">
<WrapPanel VerticalAlignment="Center">
<RadioButton Visibility="Collapsed" x:Name="rdo" IsChecked="{Binding IsChecked,RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}" GroupName="B" IsThreeState="False"/>
<TextBlock x:Name="txt" Margin="20,0" Foreground="#5A646E" FontSize="14" Text="{Binding Path=Name}" VerticalAlignment="Center" ></TextBlock>
</WrapPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="border" Property="Background" Value="#F4F6FD"></Setter>
</Trigger>
<Trigger Property="IsChecked" SourceName="rdo" Value="true">
<Setter TargetName="txt" Property="Foreground" Value="#00C18A"></Setter>
<Setter Property="IsChecked" Value="true"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</MenuItem.Template>
</MenuItem>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</TabItem>
</TabControl>
属性说明:
标记1:IsSelected用于记录上次的选中项的值
标记2:TabItemStyle在后面补充说明
标记3:ItemsControl的数据源绑定
标记4:IsChecked用于记忆上次选中项
标记5:Click用于点击下拉选项的值后,触发后续处理事件
标记6:RadioButton的IsCheck和标记4绑定的是同一个值,用于在下拉选项选中后,触发标记9事件;
标记7:Text绑定的是数据源Name属性的值;
标记8: hover样式设置;
标记9:若RadioButton被选中则触发TextBlock前景颜色变化;
(5)TabItemStyle样式实现
<Style x:Key="TabItemStyle" TargetType="{x:Type TabItem}">
<Setter Property="Height" Value="30"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border x:Name="templateRoot" SnapsToDevicePixels="True" Background="Transparent" >
<TextBlock x:Name="txt" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Header}" TextTrimming="CharacterEllipsis" />
</Border>
<ControlTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Top"/>
</MultiDataTrigger.Conditions>
<Setter Property="Foreground" TargetName="txt" Value="#999999"/>
<Setter Property="Background" TargetName="templateRoot" Value="#F0F1F6"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Top"/>
</MultiDataTrigger.Conditions>
<Setter Property="Foreground" TargetName="txt" Value="#666666"/>
<Setter Property="Background" TargetName="templateRoot" Value="White"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Top"/>
</MultiDataTrigger.Conditions>
<Setter Property="Foreground" TargetName="txt" Value="#999999"/>
<Setter Property="Background" TargetName="templateRoot" Value="#F0F1F6"/>
</MultiDataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>