一.前言

  申明WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接

  本文主要内容:

  • 下拉选择控件ComboBox的自定义样式及扩展;
  • 自定义多选控件MultiComboBox;

二.下拉选择控件ComboBox的自定义样式及扩展

2.1基本样式

  先看看基础效果图:

WPF自定义控件与样式(8)-ComboBox与自定义多选控件MultComboBox WPF自定义控件与样式(8)-ComboBox与自定义多选控件MultComboBox

  从功能上ComboBox有两种状态:

    • 可编辑文本;
    • 不可编辑文本。

从XAML的逻辑树结构上看,分为几个部分:

    • 显示当前内容的区域;
    • 下拉按钮,一般使用ToggleButton;
    • 弹出的下拉选项列表,使用Popup作为容器;

样式定义代码:  

    <!--下拉条目样式-->
    <Style TargetType="ComboBoxItem" x:Key="ComboBoxItemStyle">
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="RenderOptions.ClearTypeHint" Value="Enabled" />
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="Height" Value="28" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ComboBoxItem">
                    <Grid Background="{TemplateBinding Background}" Margin="0,0.5">
                        <Border x:Name="ItemBackground" IsHitTestVisible="False" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}"
                                Background="{TemplateBinding Background}" />
                        <ContentPresenter x:Name="contentPresenter" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" />
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter TargetName="ItemBackground" Property="Background" Value="{StaticResource ItemSelectedBackground}" />
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="ItemBackground" Property="Background" Value="{StaticResource ItemMouseOverBackground}" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!--水印:local:ControlAttachProperty.Watermark-->
    <!--Label区域:local:ControlAttachProperty.LabelTemplate ,local:ControlAttachProperty.Label-->
    <!--附加内容区域:local:ControlAttachProperty.AttachContent-->
    <!--圆角:local:ControlAttachProperty.CornerRadius-->
    <!--local:ControlAttachProperty.MouseOverBorderBrush-->
    <!--local:ControlAttachProperty.FocusBorderBrush-->
    <!--local:ControlAttachProperty.FocusBackground-->
    <Style TargetType="{x:Type ComboBox}" x:Key="DefaultComboBox">
        <Setter Property="Height" Value="30" />
        <Setter Property="Width" Value="200" />
        <Setter Property="Foreground" Value="{StaticResource TextForeground}" />
        <Setter Property="Background" Value="{StaticResource TextBackground}" />
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="BorderBrush" Value="{StaticResource ControlBorderBrush}" />
        <Setter Property="local:ControlAttachProperty.FocusBackground" Value="{StaticResource FocusBackground}" />
        <Setter Property="local:ControlAttachProperty.FocusBorderBrush" Value="{StaticResource FocusBorderBrush}" />
        <Setter Property="local:ControlAttachProperty.MouseOverBorderBrush" Value="{StaticResource MouseOverBorderBrush}" />
        <Setter Property="FontFamily" Value="{StaticResource FontFamily}" />
        <Setter Property="FontSize" Value="{StaticResource FontSize}" />
        <Setter Property="ItemContainerStyle" Value="{StaticResource ComboBoxItemStyle}" />
        <Setter Property="SnapsToDevicePixels" Value="True" />
        <Setter Property="MaxDropDownHeight" Value="400" />
        <Setter Property="ScrollViewer.CanContentScroll" Value="False" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ComboBox}">
                    <Grid x:Name="PART_Root">
                        <Border x:Name="Bg" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                CornerRadius="{TemplateBinding local:ControlAttachProperty.CornerRadius}"
                                BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" />
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="21" />
                            </Grid.ColumnDefinitions>
                            <!--Label区域-->
                            <ContentControl x:Name="Label" Template="{TemplateBinding local:ControlAttachProperty.LabelTemplate}" IsTabStop="False" IsHitTestVisible="False"
                                            Content="{TemplateBinding local:ControlAttachProperty.Label}" Margin="1,1,0,1"/>
                            <!--附加内容区域-->
                            <Border x:Name="PART_AttachContent" Panel.ZIndex="2" Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Center" >
                                <ContentControl VerticalAlignment="Center" VerticalContentAlignment="Center" Template="{TemplateBinding local:ControlAttachProperty.AttachContent}" />
                            </Border>
                            <!--下拉按钮-->
                            <ToggleButton x:Name="PART_DropDownToggle" IsTabStop="False" Style="{StaticResource ComboToggleButton}" 
                                         IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                                         Grid.Column="1"  Grid.ColumnSpan="3" IsEnabled="{Binding Path=IsReadOnly,RelativeSource={RelativeSource TemplatedParent},
                                            Converter={x:Static local:XConverter.TrueToFalseConverter},Mode=OneWay}"
                                          Background="{TemplateBinding local:ControlAttachProperty.FocusBackground}"/>
                            <!--水印-->
                            <Border Grid.Column="1">
                                <TextBlock x:Name="Message"  Padding="0" Visibility="Collapsed" Text="{TemplateBinding local:ControlAttachProperty.Watermark}" 
                                       Foreground="{TemplateBinding Foreground}" IsHitTestVisible="False" Opacity="{StaticResource WatermarkOpacity}" HorizontalAlignment="Left" TextAlignment="Center" 
                                       VerticalAlignment="Center" Margin="5,2,5,2" />
                            </Border>
                            <!--主内容区域-->
                            <Grid Grid.Column="1" x:Name="ContentSite" Margin="2 0 0 0" >
                                <ContentPresenter x:Name="PART_SelectedItem" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
                                                  ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
                                                  Content="{TemplateBinding SelectionBoxItem}" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}"
                                                  HorizontalAlignment="Stretch" Margin="2,0,2,0" 
                                                  IsHitTestVisible="False" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                                <!--文本编辑-->
                                <TextBox  x:Name="PART_EditableTextBox" Style="{StaticResource EditableTextBoxStyle}" Visibility="Collapsed" 
                                         HorizontalAlignment="Stretch" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" IsHitTestVisible="True"
                                         HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                         IsReadOnly="{TemplateBinding IsReadOnly}" FontFamily="{TemplateBinding FontFamily}" Foreground="{TemplateBinding Foreground}"
                                         Text="{TemplateBinding Text}" FontSize="{TemplateBinding FontSize}" />
                            </Grid>
                        </Grid>
                        <!--弹出下拉控件-->
                        <Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="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}}">
                                <Border x:Name="PopupBorder" BorderThickness="{TemplateBinding BorderThickness}" HorizontalAlignment="Stretch"
                                        Height="Auto" BorderBrush="{TemplateBinding BorderBrush}"
                                        Background="{StaticResource PopupBackground}">
                                </Border>
                                <ScrollViewer x:Name="DropDownScrollViewer" BorderThickness="0" Margin="1">
                                    <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" />
                                </ScrollViewer>
                            </Grid>
                        </Popup>
                    </Grid>
                    <!--触发器-->
                    <ControlTemplate.Triggers>
                        <!--1.显示水印-->
                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Value="">
                            <Setter TargetName="Message" Property="Visibility" Value="Visible" />
                        </DataTrigger>
                        <!--编辑模式-->
                        <Trigger Property="IsEditable" Value="True">
                            <Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible" />
                            <Setter TargetName="PART_SelectedItem" Property="Visibility" Value="Collapsed" />
                            <Setter TargetName="PART_DropDownToggle" Property="Grid.Column" Value="3" />
                            <Setter TargetName="PART_DropDownToggle" Property="Grid.ColumnSpan" Value="1" />
                            <Setter TargetName="PART_DropDownToggle" Property="Background" Value="Transparent" />
                            <Setter Property="IsTabStop" Value="false" />
                            <Setter TargetName="PART_DropDownToggle" Property="Focusable" Value="False" />
                        </Trigger>

                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="BorderBrush" Value="{Binding Path=(local:ControlAttachProperty.MouseOverBorderBrush),RelativeSource={RelativeSource Self}}"/>
                        </Trigger>
                        <Trigger Property="IsFocused" Value="True">
                            <Setter  Property="BorderBrush" Value="{Binding Path=(local:ControlAttachProperty.FocusBorderBrush),RelativeSource={RelativeSource Self}}"/>
                        </Trigger>
                        <Trigger Property="IsKeyboardFocusWithin" Value="True">
                            <Setter  Property="BorderBrush" Value="{Binding Path=(local:ControlAttachProperty.FocusBorderBrush),RelativeSource={RelativeSource Self}}"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="PART_Root" Property="Opacity" Value="{StaticResource DisableOpacity}"></Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
View Code

相关文章: