Elegantly override style of ComboBox's ToggleButton in WPF

后端 未结 2 1542
难免孤独
难免孤独 2021-02-19 06:13

I have a question regarding how to elegantly override an arbitrary element deep inside a control\'s visual tree. I also have attempted to resolve it in a few different ways, bu

相关标签:
2条回答
  • 2021-02-19 06:35

    There isn't an elegant solution for this. The best you can do is override the style for the entire ComboBox so that you can change the style it sets for the ToggleButton.

    You can use Blend to get the styles, however that probabliy isn't the easiest way. If you have Blend installed, go to "[Program files or where Blend is installed]\SystemThemes\WPF\areo.normalcolor.xaml".

    0 讨论(0)
  • 2021-02-19 06:37

    A Working Solution


    Hi, this style satisfies your needs, feel free to edit it as you need:

        <SolidColorBrush x:Key="WindowBackgroundBrush" Color="White" />
    
    <SolidColorBrush x:Key="MainColor" Color="DeepSkyBlue"/>
    <SolidColorBrush x:Key="MainColorLight" Color="LightSkyBlue"/>
    <SolidColorBrush x:Key="MainColorDark" Color="#00A7DF"/>
    
    <SolidColorBrush x:Key="BorderMainBrush" Color="LightGray"/>
    <SolidColorBrush x:Key="BorderDarkMainBrush" Color="#C0C0C0"/>
    
    <SolidColorBrush x:Key="BackgroundGrayDark" Color="#FFEFEFEF"/>
    <SolidColorBrush x:Key="BackgroundGrayLight" Color="#F5F5F5"/>
    
    <SolidColorBrush x:Key="ForegroundDisabledBrush" Color="DimGray"/>
    <SolidColorBrush x:Key="ForegroundBrush" Color="Black"/>
    
    
    <LinearGradientBrush x:Key="FormBackgroundBrush"
                                 EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FFFFFD" Offset="0.31" />
        <GradientStop Color="#FFF8F8F8" Offset="1" />
    </LinearGradientBrush>
    
    
    <ControlTemplate x:Key="ComboBoxToggleButton" TargetType="ToggleButton">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="20" />
            </Grid.ColumnDefinitions>
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CommonStates">
                    <VisualState x:Name="Normal"/>
                    <VisualState x:Name="MouseOver"/>
                    <VisualState x:Name="Pressed"/>
                    <VisualState x:Name="Disabled"/>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
            <Border x:Name="Border" SnapsToDevicePixels="True" Grid.ColumnSpan="2" Background="{DynamicResource BackgroundGrayDark}" BorderBrush="{DynamicResource BorderDarkMainBrush}" BorderThickness="1"  />
            <Border x:Name="Border2" Grid.Column="0" SnapsToDevicePixels="True" Margin="1" Background="{StaticResource WindowBackgroundBrush}" BorderBrush="{DynamicResource BorderDarkMainBrush}" BorderThickness="0,0,1,0" />
            <Path x:Name="Arrow" Grid.Column="1" Data="M 0 0 L 4 4 L 8 0 Z" Fill="DimGray" HorizontalAlignment="Center" VerticalAlignment="Center" />
        </Grid>
        <ControlTemplate.Triggers>
            <Trigger Property="ToggleButton.IsMouseOver" Value="true">
                <Setter Property="Background" TargetName="Border" Value="{DynamicResource MainColor}" />
                <Setter Property="BorderBrush" TargetName="Border" Value="{DynamicResource MainColor}" />
                <Setter Property="BorderBrush" TargetName="Border2" Value="{DynamicResource MainColor}" />
                <Setter Property="Fill" TargetName="Arrow" Value="White" />
            </Trigger>
            <Trigger Property="ToggleButton.IsChecked" Value="true">
                <Setter Property="Background" TargetName="Border" Value="{DynamicResource MainColorDark}" />
                <Setter Property="BorderBrush" TargetName="Border" Value="{DynamicResource MainColorDark}" />
                <Setter Property="BorderBrush" TargetName="Border2" Value="{DynamicResource MainColorDark}" />
                <Setter Property="Fill" TargetName="Arrow" Value="White" />
            </Trigger>
            <Trigger Property="IsEnabled" Value="False">
                <Setter Property="Background" TargetName="Border" Value="{DynamicResource BackgroundGrayLight}" />
                <Setter Property="BorderBrush" TargetName="Border" Value="{StaticResource BorderMainBrush}" />
                <Setter Property="Foreground" Value="{StaticResource ForegroundDisabledBrush}" />
            </Trigger>
            <DataTrigger Binding="{Binding IsKeyboardFocusWithin, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=OneWay}" Value="True">
                <Setter Property="Background" TargetName="Border" Value="{DynamicResource MainColorLight}" />
                <Setter Property="BorderBrush" TargetName="Border" Value="{DynamicResource MainColorLight}" />
                <Setter Property="BorderBrush" TargetName="Border2" Value="{DynamicResource MainColorLight}" />
                <Setter Property="Fill" TargetName="Arrow" Value="White" />
            </DataTrigger >
        </ControlTemplate.Triggers>
    </ControlTemplate>
    
    <ControlTemplate x:Key="ComboBoxTextBox" TargetType="TextBox">
        <Border x:Name="PART_ContentHost" Background="{TemplateBinding Background}" Focusable="False" />
    </ControlTemplate>
    
    <Style TargetType="ComboBox">
        <Setter Property="Validation.ErrorTemplate" Value="{x:Null}" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="OverridesDefaultStyle" Value="true" />
        <Setter Property="IsEditable" Value="True"/>
        <Setter Property="SnapsToDevicePixels" Value="true" />
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
        <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
        <Setter Property="ScrollViewer.CanContentScroll" Value="true" />
        <Setter Property="Margin" Value="2" />
        <Setter Property="MinHeight" Value="20" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ComboBox">
                    <Grid>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="MouseOver"/>
                                <VisualState x:Name="Disabled"/>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <ToggleButton x:Name="ToggleButton" Grid.Column="2" ClickMode="Press" Focusable="false"
                            IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                            Template="{StaticResource ComboBoxToggleButton}"/>
    
                        <ContentPresenter Margin="3,3,23,3" Content="{TemplateBinding SelectionBoxItem}"
                            ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
                            ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
                            HorizontalAlignment="Left" IsHitTestVisible="False" x:Name="ContentSite"
                            VerticalAlignment="Center" />
    
                        <TextBox Style="{x:Null}" x:Name="PART_EditableTextBox" Margin="3,3,23,3" Background="Transparent"
                            Focusable="True" HorizontalAlignment="Left" IsReadOnly="{TemplateBinding IsReadOnly}"
                            Template="{StaticResource ComboBoxTextBox}" VerticalAlignment="Center" Visibility="Hidden" />
    
                        <Popup AllowsTransparency="True" Focusable="False" IsOpen="{TemplateBinding IsDropDownOpen}" x:Name="Popup" Placement="Bottom" PopupAnimation="Fade">
                            <Grid MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{TemplateBinding ActualWidth}" x:Name="DropDown" SnapsToDevicePixels="True">
                                <Border x:Name="DropDownBorder" Background="White" BorderBrush="{StaticResource BorderDarkMainBrush}" BorderThickness="1" CornerRadius="0" />
                                <ScrollViewer Margin="2" SnapsToDevicePixels="True">
                                    <StackPanel KeyboardNavigation.DirectionalNavigation="Contained" IsItemsHost="True" TextBlock.Foreground="Black" />
                                </ScrollViewer>
                            </Grid>
                        </Popup>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="HasItems" Value="false">
                            <Setter Property="MinHeight" TargetName="DropDownBorder" Value="95" />
                        </Trigger>
                        <Trigger Property="IsGrouping" Value="true">
                            <Setter Property="ScrollViewer.CanContentScroll" Value="false" />
                        </Trigger>
                        <Trigger Property="IsEditable" Value="true">
                            <Setter Property="IsTabStop" Value="false" />
                            <Setter Property="Visibility" TargetName="PART_EditableTextBox" Value="Visible" />
                            <Setter Property="Visibility" TargetName="ContentSite" Value="Hidden" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
        </Style.Triggers>
    </Style>
    

    Note the DataTrigger part inside the toggle button style, it hooks to its templated parent's IsKeyboardFocusWithin property istead of IsFocused property, because the last one won't work if you set the ComboBox.IsEditable to True as I did in this style.

    <DataTrigger Binding="{Binding IsKeyboardFocusWithin, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=OneWay}" Value="True">
        <Setter Property="Background" TargetName="Border" Value="{DynamicResource MainColorLight}" />
        <Setter Property="BorderBrush" TargetName="Border" Value="{DynamicResource MainColorLight}" />
        <Setter Property="BorderBrush" TargetName="Border2" Value="{DynamicResource MainColorLight}" />
        <Setter Property="Fill" TargetName="Arrow" Value="White" />
    </DataTrigger >
    
    0 讨论(0)
提交回复
热议问题