简体   繁体   中英

Styling the indeterminate state of a WPF checkbox

I want to style the indeterminate state of a WPF checkbox. We have a treeview control with checkboxes, and we want the indeterminate state to represent that some descendants are checked and some are unchecked.

The solution I'm seeing everywhere is to override the default control template for a checkbox and do what I need to do.

I have two problems with that:

  1. I can't find the control template for a normal Aero checkbox. This one: http://msdn.microsoft.com/en-us/library/ms752319.aspx looks goofy.

  2. The control template I get from Expression Blend has that BulletChrome element in it, and I can't figure out what to do with that.

So does anyone know where to get a checkbox control template that looks "normal" or is there an easier way to just style the indeterminate state by itself?

I'm sure there's an easy way I'm just overlooking... Right?

Try this (modified from the article that publicgk linked to)

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:theme="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero" Title="MainWindow" Height="350" Width="525">
  <Window.Resources>
    <Style x:Key="CheckRadioFocusVisual">
      <Setter Property="Control.Template">
        <Setter.Value>
          <ControlTemplate>
            <Rectangle Margin="14,0,0,0"
                       StrokeThickness="1"
                       Stroke="Black"
                       StrokeDashArray="1 2"
                       SnapsToDevicePixels="true"/>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>

    <Style x:Key="EmptyCheckBoxFocusVisual">
      <Setter Property="Control.Template">
        <Setter.Value>
          <ControlTemplate>
            <Rectangle Margin="1"
                       StrokeThickness="1"
                       Stroke="Black"
                       StrokeDashArray="1 2"
                       SnapsToDevicePixels="true"/>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>

    <SolidColorBrush x:Key="CheckBoxFillNormal"
                     Color="#F4F4F4"/>
    <SolidColorBrush x:Key="CheckBoxStroke"
                     Color="#8E8F8F"/>
    <Style x:Key="{x:Type CheckBox}"
         TargetType="{x:Type CheckBox}">
      <Setter Property="Foreground"
            Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
      <Setter Property="Background"
            Value="{StaticResource CheckBoxFillNormal}"/>
      <Setter Property="BorderBrush"
            Value="{StaticResource CheckBoxStroke}"/>
      <Setter Property="BorderThickness"
            Value="1"/>
      <Setter Property="FocusVisualStyle"
            Value="{StaticResource EmptyCheckBoxFocusVisual}"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type CheckBox}">
            <BulletDecorator Background="Transparent" 
                             SnapsToDevicePixels="true">
              <BulletDecorator.Bullet>
                <theme:BulletChrome Background="{TemplateBinding Background}"
                                    BorderBrush="{TemplateBinding BorderBrush}"
                                    RenderMouseOver="{TemplateBinding IsMouseOver}"
                                    RenderPressed="{TemplateBinding IsPressed}"
                                    IsChecked="{TemplateBinding IsChecked}"/>
              </BulletDecorator.Bullet>
              <ContentPresenter Margin="{TemplateBinding Padding}"
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                RecognizesAccessKey="True"
                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
            </BulletDecorator>
            <ControlTemplate.Triggers>
              <Trigger Property="IsChecked" 
                       Value="{x:Null}">
                <!-- TODO: Do Stuff Here -->
              </Trigger>
              <Trigger Property="HasContent"
                       Value="true">
                <Setter Property="FocusVisualStyle"
                        Value="{StaticResource CheckRadioFocusVisual}"/>
                <Setter Property="Padding"
                        Value="4,0,0,0"/>
              </Trigger>
              <Trigger Property="IsEnabled"
                       Value="false">
                <Setter Property="Foreground"
                        Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </Window.Resources>
  <StackPanel>
    <CheckBox IsChecked="True" Content="Checked"/>
    <CheckBox IsChecked="{x:Null}" Content="Unknown"/>
    <CheckBox IsChecked="False" Content="Not Checked"/>
  </StackPanel>
</Window>

Have you already tried to download the Aero Theme xaml and looked into it?
Where can I download Microsoft's standard WPF themes from?

Call me crazy, but I've actually reimplemented standard Aero checkbox in pure XAML. If you want to customize Aero checkbox, it's a good starting point. You can find other styles in my repository on GitHub ( specific commit , in case files are moved).

BulletCommon.xaml (common resources for CheckBox and RadioButton)

<ResourceDictionary
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:xx="clr-namespace:Alba.WpfThemeGenerator.Markup">

    <!-- Colors -->

    <!-- Background overlay -->
    <SolidColorBrush x:Key="Bullet.BackgroundOverlay.Hover" Color="#DEF9FA"/>
    <SolidColorBrush x:Key="Bullet.BackgroundOverlay.Pressed" Color="#C2E4F6"/>
    <SolidColorBrush x:Key="Bullet.BackgroundOverlay.Disabled" Color="#F4F4F4"/>
    <!-- Border overlay -->
    <SolidColorBrush x:Key="Bullet.BorderOverlay.Hover" Color="#3C7FB1"/>
    <SolidColorBrush x:Key="Bullet.BorderOverlay.Pressed" Color="#2C628B"/>
    <SolidColorBrush x:Key="Bullet.BorderOverlay.Disabled" Color="#ADB2B5"/>
    <!-- Inner border -->
    <LinearGradientBrush x:Key="Bullet.InnerBorder.Disabled" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#E1E3E5" Offset="0.25"/>
        <GradientStop Color="#E8E9EA" Offset="0.5"/>
        <GradientStop Color="#F3F3F3" Offset="1"/>
    </LinearGradientBrush>
    <!-- Indeterminate inner border -->
    <LinearGradientBrush x:Key="Bullet.InnerBorder.IndeterminateDisabled" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#BFD0DD" Offset="0"/>
        <GradientStop Color="#BDCBD7" Offset="0.5"/>
        <GradientStop Color="#BAC4CC" Offset="1"/>
    </LinearGradientBrush>
    <!-- Inner fill -->
    <LinearGradientBrush x:Key="Bullet.InnerFill.Normal" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#CBCFD5" Offset="0.2"/>
        <GradientStop Color="#F7F7F7" Offset="0.8"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="Bullet.InnerFill.Hover" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#B1DFFD" Offset="0.2"/>
        <GradientStop Color="#E9F7FE" Offset="0.8"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="Bullet.InnerFill.Pressed" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#7FBADC" Offset="0.2"/>
        <GradientStop Color="#D6EDF9" Offset="0.8"/>
    </LinearGradientBrush>
    <!-- Indeterminate fill -->
    <LinearGradientBrush x:Key="Bullet.Fill.Indeterminate" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#2FA8D5" Offset="0.2"/>
        <GradientStop Color="#25598C" Offset="0.8"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="Bullet.Fill.IndeterminateHover" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#33D7ED" Offset="0.2"/>
        <GradientStop Color="#2094CE" Offset="0.8"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="Bullet.Fill.IndeterminatePressed" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#17447A" Offset="0.2"/>
        <GradientStop Color="#218BC3" Offset="0.8"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="Bullet.Fill.IndeterminateDisabled" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#C0E5F3" Offset="0.2"/>
        <GradientStop Color="#BDCDDC" Offset="0.8"/>
    </LinearGradientBrush>

    <!-- Styles -->

    <Style x:Key="Bullet.FocusVisual.Normal">
        <Setter Property="Control.Template">
            <Setter.Value>
                <ControlTemplate>
                    <Rectangle Margin="14,0,0,0" SnapsToDevicePixels="True"
                            StrokeThickness="1" Stroke="{xx:SystemBrush ControlText}" StrokeDashArray="1 2"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="Bullet.FocusVisual.Empty">
        <Setter Property="Control.Template">
            <Setter.Value>
                <ControlTemplate>
                    <Rectangle Margin="1" SnapsToDevicePixels="True"
                            StrokeThickness="1" Stroke="{xx:SystemBrush ControlText}" StrokeDashArray="1 2"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

CheckBox.xaml (resources for CheckBox)

<ResourceDictionary
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:xx="clr-namespace:Alba.WpfThemeGenerator.Markup">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="BulletCommon.xaml"/>
    </ResourceDictionary.MergedDictionaries>

    <!-- Colors -->

    <SolidColorBrush x:Key="CheckBox.Stroke" Color="#8E8F8F"/>
    <SolidColorBrush x:Key="CheckBox.Fill" Color="#F4F4F4"/>
    <!-- Check mark -->
    <SolidColorBrush x:Key="CheckBox.Glyph.Stroke.Normal" Color="#FFFFFF"/>
    <SolidColorBrush x:Key="CheckBox.Glyph.Stroke.Pressed" Color="#B2FFFFFF"/>
    <SolidColorBrush x:Key="CheckBox.Glyph.Fill.Normal" Color="#31347C"/>
    <SolidColorBrush x:Key="CheckBox.Glyph.Fill.Pressed" Color="#B231347C"/>
    <SolidColorBrush x:Key="CheckBox.Glyph.Fill.Disabled" Color="#AEB7CF"/>
    <!-- Inner border -->
    <LinearGradientBrush x:Key="CheckBox.InnerBorder" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#AEB3B9" Offset="0.25"/>
        <GradientStop Color="#C2C4C6" Offset="0.5"/>
        <GradientStop Color="#EAEBEB" Offset="1"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="CheckBox.InnerBorder.Hover" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#79C6F9" Offset="0.3"/>
        <GradientStop Color="#79C6F9" Offset="0.5"/>
        <GradientStop Color="#D2EDFD" Offset="1"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="CheckBox.InnerBorder.Pressed" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#54A6D5" Offset="0.3"/>
        <GradientStop Color="#5EB5E4" Offset="0.5"/>
        <GradientStop Color="#C4E5F6" Offset="1"/>
    </LinearGradientBrush>
    <!-- Indeterminate inner border -->
    <LinearGradientBrush x:Key="CheckBox.InnerBorder.Indeterminate" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#2A628D" Offset="0"/>
        <GradientStop Color="#245479" Offset="0.5"/>
        <GradientStop Color="#193B55" Offset="1"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="CheckBox.InnerBorder.IndeterminateHover" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#29628D" Offset="0"/>
        <GradientStop Color="#245479" Offset="0.5"/>
        <GradientStop Color="#193B55" Offset="1"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="CheckBox.InnerBorder.IndeterminatePressed" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#193B55" Offset="0"/>
        <GradientStop Color="#245479" Offset="0.5"/>
        <GradientStop Color="#29628D" Offset="1"/>
    </LinearGradientBrush>
    <!-- Indeterminate highlight -->
    <LinearGradientBrush x:Key="CheckBox.Highlight.Indeterminate" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#80FFFFFF" Offset="0"/>
        <GradientStop Color="#00FFFFFF" Offset="0.5"/>
        <GradientStop Color="#003333A0" Offset="0.5"/>
        <GradientStop Color="#003333A0" Offset="1"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="CheckBox.Highlight.IndeterminateHover" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#80FFFFFF" Offset="0"/>
        <GradientStop Color="#00FFFFFF" Offset="0.5"/>
        <GradientStop Color="#003333A0" Offset="0.5"/>
        <GradientStop Color="#803333A0" Offset="1"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="CheckBox.Highlight.IndeterminatePressed" StartPoint="0,0" EndPoint="1,1">
        <GradientStop Color="#00FFFFFF" Offset="0.5"/>
        <GradientStop Color="#20FFFFFF" Offset="1"/>
    </LinearGradientBrush>

    <!-- Images -->

    <PathGeometry x:Key="CheckBox.Glyph.Geometry">
        <PathFigure StartPoint="9.0, 1.833" IsClosed="True">
            <LineSegment Point="10.667, 3.167"/>
            <LineSegment Point="7, 10.667"/>
            <LineSegment Point="5.333, 10.667"/>
            <LineSegment Point="3.333, 8.167"/>
            <LineSegment Point="3.333, 6.833"/>
            <LineSegment Point="4.833, 6.5"/>
            <LineSegment Point="6, 8"/>
        </PathFigure>
    </PathGeometry>

    <!-- Styles -->

    <Style x:Key="{x:Type CheckBox}" TargetType="{x:Type CheckBox}">
        <Setter Property="FocusVisualStyle" Value="{StaticResource Bullet.FocusVisual.Empty}"/>
        <Setter Property="Foreground" Value="{xx:SystemBrush ControlText}"/>
        <Setter Property="Background" Value="{StaticResource CheckBox.Fill}"/>
        <Setter Property="BorderBrush" Value="{StaticResource CheckBox.Stroke}"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type CheckBox}">
                    <BulletDecorator Background="Transparent" SnapsToDevicePixels="True">
                        <BulletDecorator.Bullet>
                            <Grid Width="13" Height="13">
                                <Rectangle x:Name="Background" Margin="0" Fill="{TemplateBinding Background}"/>
                                <Rectangle x:Name="BackgroundOverlay" Margin="0"/>
                                <Rectangle x:Name="InnerFill" Margin="3" Fill="{StaticResource Bullet.InnerFill.Normal}"/>
                                <Rectangle x:Name="InnerBorder" Margin="2" Stroke="{StaticResource CheckBox.InnerBorder}"/>
                                <Rectangle x:Name="Highlight" Margin="3"/>
                                <Path x:Name="GlyphStroke" Margin="0" StrokeThickness="1.5" Data="{StaticResource CheckBox.Glyph.Geometry}"/>
                                <Path x:Name="GlyphFill" Margin="0" Data="{StaticResource CheckBox.Glyph.Geometry}"/>
                                <Rectangle x:Name="Border" Margin="0" Stroke="{TemplateBinding BorderBrush}"/>
                                <Rectangle x:Name="BorderOverlay" Margin="0"/>
                            </Grid>
                        </BulletDecorator.Bullet>
                        <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Margin="{TemplateBinding Padding}"
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                RecognizesAccessKey="True"/>
                    </BulletDecorator>
                    <ControlTemplate.Triggers>

                        <Trigger Property="HasContent" Value="True">
                            <!-- if (HasContent) -->
                            <Setter Property="FocusVisualStyle" Value="{StaticResource Bullet.FocusVisual.Normal}"/>
                            <Setter Property="Padding" Value="4,0,0,0"/>
                        </Trigger>

                        <Trigger Property="IsMouseOver" Value="True">
                            <!-- if (IsMouseOver) -->
                            <Setter TargetName="BackgroundOverlay" Property="Fill" Value="{StaticResource Bullet.BackgroundOverlay.Hover}"/>
                            <Setter TargetName="InnerFill" Property="Fill" Value="{StaticResource Bullet.InnerFill.Hover}"/>
                            <Setter TargetName="BorderOverlay" Property="Stroke" Value="{StaticResource Bullet.BorderOverlay.Hover}"/>
                            <Setter TargetName="InnerBorder" Property="Stroke" Value="{StaticResource CheckBox.InnerBorder.Hover}"/>
                        </Trigger>
                        <Trigger Property="IsChecked" Value="{x:Null}">
                            <!-- if (IsChecked == null) -->
                            <Setter TargetName="Highlight" Property="Stroke" Value="{StaticResource CheckBox.Highlight.Indeterminate}"/>
                            <Setter TargetName="InnerFill" Property="Fill" Value="{StaticResource Bullet.Fill.Indeterminate}"/>
                            <Setter TargetName="InnerBorder" Property="Stroke" Value="{StaticResource CheckBox.InnerBorder.Indeterminate}"/>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <!-- if (IsPressed) -->
                            <Setter TargetName="BackgroundOverlay" Property="Fill" Value="{StaticResource Bullet.BackgroundOverlay.Pressed}"/>
                            <Setter TargetName="InnerFill" Property="Fill" Value="{StaticResource Bullet.InnerFill.Pressed}"/>
                            <Setter TargetName="BorderOverlay" Property="Stroke" Value="{StaticResource Bullet.BorderOverlay.Pressed}"/>
                            <Setter TargetName="InnerBorder" Property="Stroke" Value="{StaticResource CheckBox.InnerBorder.Pressed}"/>
                        </Trigger>
                        <MultiTrigger>
                            <!-- if (IsChecked == null && IsMouseOver) -->
                            <MultiTrigger.Conditions>
                                <Condition Property="IsChecked" Value="{x:Null}"/>
                                <Condition Property="IsMouseOver" Value="True"/>
                            </MultiTrigger.Conditions>
                            <Setter TargetName="Highlight" Property="Stroke" Value="{StaticResource CheckBox.Highlight.IndeterminateHover}"/>
                            <Setter TargetName="InnerFill" Property="Fill" Value="{StaticResource Bullet.Fill.IndeterminateHover}"/>
                            <Setter TargetName="InnerBorder" Property="Stroke" Value="{StaticResource CheckBox.InnerBorder.IndeterminateHover}"/>
                        </MultiTrigger>

                        <MultiTrigger>
                            <!-- if (IsChecked == null && IsPressed) -->
                            <MultiTrigger.Conditions>
                                <Condition Property="IsChecked" Value="{x:Null}"/>
                                <Condition Property="IsPressed" Value="True"/>
                            </MultiTrigger.Conditions>
                            <Setter TargetName="Highlight" Property="Stroke" Value="{StaticResource CheckBox.Highlight.IndeterminatePressed}"/>
                            <Setter TargetName="InnerFill" Property="Fill" Value="{StaticResource Bullet.Fill.IndeterminatePressed}"/>
                            <Setter TargetName="InnerBorder" Property="Stroke" Value="{StaticResource CheckBox.InnerBorder.IndeterminatePressed}"/>
                        </MultiTrigger>
                        <MultiTrigger>
                            <!-- if (IsChecked == true && IsPressed) -->
                            <MultiTrigger.Conditions>
                                <Condition Property="IsChecked" Value="True"/>
                                <Condition Property="IsPressed" Value="True"/>
                            </MultiTrigger.Conditions>
                            <Setter TargetName="GlyphStroke" Property="Stroke" Value="{StaticResource CheckBox.Glyph.Stroke.Pressed}"/>
                            <Setter TargetName="GlyphFill" Property="Fill" Value="{StaticResource CheckBox.Glyph.Fill.Pressed}"/>
                        </MultiTrigger>
                        <MultiTrigger>
                            <!-- if (IsChecked == true && !IsPressed) -->
                            <MultiTrigger.Conditions>
                                <Condition Property="IsChecked" Value="True"/>
                                <Condition Property="IsPressed" Value="False"/>
                            </MultiTrigger.Conditions>
                            <Setter TargetName="GlyphStroke" Property="Stroke" Value="{StaticResource CheckBox.Glyph.Stroke.Normal}"/>
                            <Setter TargetName="GlyphFill" Property="Fill" Value="{StaticResource CheckBox.Glyph.Fill.Normal}"/>
                        </MultiTrigger>

                        <Trigger Property="IsEnabled" Value="False">
                            <!-- if (!IsEnabled) -->
                            <Setter Property="Foreground" Value="{xx:SystemBrush GrayText}"/>
                            <Setter TargetName="InnerFill" Property="Fill" Value="{x:Null}"/>
                            <Setter TargetName="GlyphStroke" Property="Stroke" Value="{x:Null}"/>
                            <Setter TargetName="BorderOverlay" Property="Stroke" Value="{StaticResource Bullet.BorderOverlay.Disabled}"/>
                            <Setter TargetName="InnerBorder" Property="Stroke" Value="{StaticResource Bullet.InnerBorder.Disabled}"/>
                        </Trigger>
                        <MultiTrigger>
                            <!-- if (IsChecked == null && !IsEnabled) -->
                            <MultiTrigger.Conditions>
                                <Condition Property="IsChecked" Value="{x:Null}"/>
                                <Condition Property="IsEnabled" Value="False"/>
                            </MultiTrigger.Conditions>
                            <Setter TargetName="InnerFill" Property="Fill" Value="{StaticResource Bullet.Fill.IndeterminateDisabled}"/>
                            <Setter TargetName="InnerBorder" Property="Stroke" Value="{StaticResource Bullet.InnerBorder.IndeterminateDisabled}"/>
                        </MultiTrigger>
                        <MultiTrigger>
                            <!-- if (IsChecked == true && !IsEnabled) -->
                            <MultiTrigger.Conditions>
                                <Condition Property="IsChecked" Value="True"/>
                                <Condition Property="IsEnabled" Value="False"/>
                            </MultiTrigger.Conditions>
                            <Setter TargetName="GlyphFill" Property="Fill" Value="{StaticResource CheckBox.Glyph.Fill.Disabled}"/>
                        </MultiTrigger>

                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

Notes:

  1. {xx:SystemBrush ControlText} is a shortcut for {DynamicResource {x:Static SystemColors.ControlTextBrushKey}} . You can either use that shortcut or just find and replace with regex.

  2. RTL, animations, weird cases are not supported.

  3. This style is slower than using BulletChrome which is heavily optimized.

You can use CheckBox styling from the classic theme located at:

  • C:\\Program Files (x86)\\Microsoft Expression\\Blend 4\\SystemThemes\\Wpf\\classic.xaml

This implementation has a Path representing the checkbox mark called CheckMarkPath . I simply replaced this Path with a filled Rectangle to get these results:

复选框

You can set the checkbox's IsThreeState property to true.

This, however, allows toggling the checkbox's value to null.
If that is undesired, you can instead add to your CheckBox's Template a trigger for the null value, just like Greg Bacchus shows in his answer :

<ControlTemplate.Triggers>
    <Trigger Property="IsChecked" Value="{x:Null}">
        <!-- TODO: Do Stuff Here -->
    </Trigger>
</ControlTemplate.Triggers>

The below code is a sample check box with three state.

<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
    <!-- Focus Style -->

    <SolidColorBrush x:Key="InputBackgroundFocused" Color="Orange"></SolidColorBrush>

    <Style x:Key="CheckBoxFocusVisualStyle">
        <Setter Property="Control.Template">
            <Setter.Value>
                <ControlTemplate>
                    <Border CornerRadius="2" Margin="0,0,4,3" BorderThickness="2" BorderBrush="{StaticResource InputBackgroundFocused}" Background="Transparent"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- Fill Brushes -->

    <LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
        <GradientBrush.GradientStops>
            <GradientStopCollection>
                <GradientStop Color="#FFF" Offset="0.0"/>
                <GradientStop Color="#CCC" Offset="1.0"/>
            </GradientStopCollection>
        </GradientBrush.GradientStops>
    </LinearGradientBrush>

    <LinearGradientBrush x:Key="HorizontalNormalBrush" StartPoint="0,0" EndPoint="1,0">
        <GradientBrush.GradientStops>
            <GradientStopCollection>
                <GradientStop Color="#FFF" Offset="0.0"/>
                <GradientStop Color="#CCC" Offset="1.0"/>
            </GradientStopCollection>
        </GradientBrush.GradientStops>
    </LinearGradientBrush>

    <LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
        <GradientBrush.GradientStops>
            <GradientStopCollection>
                <GradientStop Color="#FFF" Offset="0.0"/>
                <GradientStop Color="#EEE" Offset="1.0"/>
            </GradientStopCollection>
        </GradientBrush.GradientStops>
    </LinearGradientBrush>

    <LinearGradientBrush x:Key="HorizontalLightBrush" StartPoint="0,0" EndPoint="1,0">
        <GradientBrush.GradientStops>
            <GradientStopCollection>
                <GradientStop Color="#FFF" Offset="0.0"/>
                <GradientStop Color="#EEE" Offset="1.0"/>
            </GradientStopCollection>
        </GradientBrush.GradientStops>
    </LinearGradientBrush>

    <LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
        <GradientBrush.GradientStops>
            <GradientStopCollection>
                <GradientStop Color="#FFF" Offset="0.0"/>
                <GradientStop Color="#AAA" Offset="1.0"/>
            </GradientStopCollection>
        </GradientBrush.GradientStops>
    </LinearGradientBrush>

    <LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
        <GradientBrush.GradientStops>
            <GradientStopCollection>
                <GradientStop Color="#BBB" Offset="0.0"/>
                <GradientStop Color="#EEE" Offset="0.1"/>
                <GradientStop Color="#EEE" Offset="0.9"/>
                <GradientStop Color="#FFF" Offset="1.0"/>
            </GradientStopCollection>
        </GradientBrush.GradientStops>
    </LinearGradientBrush>

    <SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />

    <SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />

    <SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />

    <SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />

    <!-- Border Brushes -->

    <LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
        <GradientBrush.GradientStops>
            <GradientStopCollection>
                <GradientStop Color="#CCC" Offset="0.0"/>
                <GradientStop Color="#444" Offset="1.0"/>
            </GradientStopCollection>
        </GradientBrush.GradientStops>
    </LinearGradientBrush>

    <LinearGradientBrush x:Key="HorizontalNormalBorderBrush" StartPoint="0,0" EndPoint="1,0">
        <GradientBrush.GradientStops>
            <GradientStopCollection>
                <GradientStop Color="#CCC" Offset="0.0"/>
                <GradientStop Color="#444" Offset="1.0"/>
            </GradientStopCollection>
        </GradientBrush.GradientStops>
    </LinearGradientBrush>

    <LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">
        <GradientBrush.GradientStops>
            <GradientStopCollection>
                <GradientStop Color="#777" Offset="0.0"/>
                <GradientStop Color="#000" Offset="1.0"/>
            </GradientStopCollection>
        </GradientBrush.GradientStops>
    </LinearGradientBrush>

    <LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
        <GradientBrush.GradientStops>
            <GradientStopCollection>
                <GradientStop Color="#444" Offset="0.0"/>
                <GradientStop Color="#888" Offset="1.0"/>
            </GradientStopCollection>
        </GradientBrush.GradientStops>
    </LinearGradientBrush>

    <SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />

    <SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />

    <SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />

    <!-- Miscellaneous Brushes -->
    <SolidColorBrush x:Key="GlyphBrush" Color="#444" />

    <SolidColorBrush x:Key="LightColorBrush" Color="#DDD" />
    <Style TargetType="{x:Type CheckBox}">
        <Setter Property="SnapsToDevicePixels" Value="true"/>
        <Setter Property="OverridesDefaultStyle" Value="true"/>
        <Setter Property="FocusVisualStyle" Value="{DynamicResource CheckBoxFocusVisualStyle}" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="CheckBox">
                    <BulletDecorator Background="Transparent">
                        <BulletDecorator.Bullet>
                            <Border x:Name="Border"  
                              Width="17" 
                              Height="17" 
                              CornerRadius="2" 
                              Background="{StaticResource NormalBrush}"
                              BorderThickness="1"
                              BorderBrush="{StaticResource NormalBorderBrush}">
                                <Path 
                                Width="11" Height="11" 
                                x:Name="CheckMark"
                                SnapsToDevicePixels="False" 
                                Stroke="{StaticResource GlyphBrush}"
                                StrokeThickness="2"
                                Data="M 2,4 C 2,4 3,5 5,13 C 5,13 5,3 12,0" />
                            </Border>
                        </BulletDecorator.Bullet>
                        <ContentPresenter Margin="4,0,0,0"
                            VerticalAlignment="Center"
                            HorizontalAlignment="Left"
                            RecognizesAccessKey="True"/>
                    </BulletDecorator>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsChecked" Value="false">
                            <Setter TargetName="CheckMark" Property="Visibility" Value="Collapsed"/>
                        </Trigger>
                        <Trigger Property="IsChecked" Value="{x:Null}">
                            <Setter TargetName="CheckMark" Property="Data" Value="M 0 7 L 7 0" />
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter TargetName="Border" Property="Background" Value="{StaticResource DarkBrush}" />
                        </Trigger>
                        <Trigger Property="IsPressed" Value="true">
                            <Setter TargetName="Border" Property="Background" Value="{StaticResource PressedBrush}" />
                            <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource PressedBorderBrush}" />
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
                            <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DisabledBorderBrush}" />
                            <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>

            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<Grid>
    <StackPanel>
    <CheckBox>Hello</CheckBox>
        <CheckBox IsThreeState="True">asdfsdaf</CheckBox>
        </StackPanel>
</Grid>

The best software to modify the existing look and feel of the controls is Expression Blend. If you want to change the existing look and feel and you have to modify the Control Template of the control.

Another option may be to style a toggle button as it has multiple states as well. In past projects I have created custom toggle buttons with multiple states that look and function as chekcboxes. I did use blend to make the changes but in using a tggle button as the base I was able to create a more customized look and feel for the various states of the button/chekcbox. Using a toggle button can bypass a lot of the chrome related issues that are tightly boud to a standard chekcbox control.

Have you thought about using SimpleStyles as the base for control?

ExpressionSimpleStyles

By selecting this control from the Assets panel, Expression Blend will place a new resource dictonary in your project that you can use to modify the style as you please. Might be a better starting point than trying to hack into the Chrome for the standard checkbox.

Using the template from Blend:

To make BulletChrome work, you need to add a reference to PresentationFramework.Aero , and add the xml namespace declaration for the "theme" namespace:

xmlns:theme="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"

I didn't try this myself but I believe it should work (I've done it with Luna).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM