As explained in the following article http://vbcity.com/blogs/xtab/archive/2009/12/15/wpf-using-a-virtualizingstackpanel-to-improve-combobox-performance.aspx, I use the VirtualizingStackPanel to improve the performance of my project's ComboBoxes.
And it works great ... until I apply a style to ComboBox to change the layout (I found the style here : http://msdn.microsoft.com/en-us/library/ee230084.aspx)
Here is the source code of a sample which demonstrate the matter (figuring that ItemSource propery of the ComboBox are filled with 10 000 items).
If anyone has an idea ...
<Window.Resources>
<!-- 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="Black" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#E5E5E5" />
<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="#AAA" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBorderBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#AAA" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#AAA" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#AAA" Offset="0.0"/>
<GradientStop Color="#AAA" 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" />
<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="ToggleButton">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="16" />
</Grid.ColumnDefinitions>
<Border x:Name="Border" Grid.ColumnSpan="2" CornerRadius="2" Background="{StaticResource NormalBrush}" BorderBrush="{StaticResource NormalBorderBrush}" BorderThickness="1" />
<Border x:Name="Border2" Grid.Column="0" CornerRadius="2,0,0,2" Margin="1" Background="{StaticResource WindowBackgroundBrush}" BorderBrush="{StaticResource NormalBorderBrush}" BorderThickness="0,0,1,0" />
<Path x:Name="Arrow" Grid.Column="1" Fill="{StaticResource GlyphBrush}" HorizontalAlignment="Center" VerticalAlignment="Center" Data="M 0 0 L 4 4 L 8 0 Z"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource DarkBrush}" />
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource PressedBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DisabledBorderBrush}" />
<Setter TargetName="Border2" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
<Setter TargetName="Border2" Property="BorderBrush" Value="{StaticResource DisabledBorderBrush}" />
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
<Setter TargetName="Arrow" Property="Fill" Value="{StaticResource DisabledForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="ComboBoxTextBox" TargetType="TextBox">
<Border x:Name="PART_ContentHost" Focusable="False" Background="{TemplateBinding Background}" />
</ControlTemplate>
<Style x:Key="ComboBoxBaseStyle" TargetType="ComboBox">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="MinHeight" Value="20"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<ToggleButton Name="ToggleButton" Template="{StaticResource ComboBoxToggleButton}" Grid.Column="2" Focusable="false" IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press">
</ToggleButton>
<ContentPresenter Name="ContentSite" IsHitTestVisible="False" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Margin="3,3,23,3" VerticalAlignment="Center" HorizontalAlignment="Left" />
<TextBox x:Name="PART_EditableTextBox" Style="{x:Null}" Template="{StaticResource ComboBoxTextBox}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="3,3,23,3" Focusable="True" Background="Transparent" Visibility="Hidden" IsReadOnly="{TemplateBinding IsReadOnly}"/>
<Popup Name="Popup" Placement="Bottom" IsOpen="{TemplateBinding IsDropDownOpen}" AllowsTransparency="True" Focusable="False" PopupAnimation="Slide">
<Grid Name="DropDown" SnapsToDevicePixels="True" MinWidth="{TemplateBinding ActualWidth}" MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border x:Name="DropDownBorder" Background="{StaticResource WindowBackgroundBrush}" BorderThickness="1" BorderBrush="{StaticResource SolidBorderBrush}"/>
<ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
<Trigger SourceName="Popup" Property="Popup.AllowsTransparency" Value="true">
<Setter TargetName="DropDownBorder" Property="CornerRadius" Value="4"/>
<Setter TargetName="DropDownBorder" Property="Margin" Value="0,2,0,0"/>
</Trigger>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
<Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<ComboBox x:Name="CustomerComboBox_WithoutStyle" SelectedValuePath="Id" DisplayMemberPath="Text" >
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
<ComboBox x:Name="CustomerComboBox_WithStyle" SelectedValuePath="Id" DisplayMemberPath="Text" Style="{StaticResource ComboBoxBaseStyle}" >
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
</StackPanel>
The problem of your style is that it does not use any kind of UI Virtualization.
Try replacing the StackPanel with a VirtualizingStackpanel and set the VirtualizationMode to Recycle and you will see a huge performance boost.
Your approach forces WPF to realize a container and FrameworkElement for each of your 10k items, which takes an eternity as you have only 1 UI thread.
Just replace
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
with
<VirtualizingStackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
and see if it gets faster.
To be more precise, you override the ItemsPanel in your derived styles, but have no "ItemsPresenter" in the base style. Instead to use the StackPanel "hardcoded"
So many items probably is the result of a (UI) design flaw.
Try using a listbox or multiple combo boxes where the first combo box is a category and second is a sub category of the first.
having 10000 items in a combox is not very good design. MaxDropDownItems might be able to help you as less rendering is required. (this is the number of visible items shown when clicking the dropdown)
But, you should consider using a listbox. When you apply a listbox you should uses pages and on each page have 1000 records. Or use a search box
Related
I'm trying to create a straight-forward, colored button in WPF that changes colors when the mouse is over and again when the button is pressed. I've been working in web projects lately and it's insane how much more complicated styling WPF is vs. HTML.
Commentary aside, although the MS documentation on UIElement.IsMouseOver states that it includes child elements, I'm experiencing the opposite. My trigger changes the color when my pointer is right over the border of the button, but as soon as it moves a few pixels farther inward, it changes back. Using Snoop, as I move the pointer pixel by pixel toward the center of the button, I can see that IsMouseOver is true for the border, then for the ContentPresenter, then for the inner TextBlock, but they are mutually exclusive. When IsMouseOver is true for a child element, it's false for the parents. I've seen lots of examples using the same approach as mine, but it's not working. I've gone through quite a few edits so maybe I have some erroneous syntax. How do I make the trigger apply when the mouse is anywhere within the button.
Here is my code. I define the brushes as resources of the button, override the default control template replacing the Chrome border with a regular Border, and then replace the brushes in Style Triggers.
<Grid Name="grdResults" Grid.Row="3" Margin="0" Visibility="Visible">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
...
</Grid.RowDefinitions>
<Button Grid.ColumnSpan="2" Grid.RowSpan="2" Content="X" BorderThickness="1" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,0,3,0" Padding="5,1">
<Button.Resources>
<Style x:Key="ButtonFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Fill="Transparent" Stroke="WhiteSmoke" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<LinearGradientBrush x:Key="BorderBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF9B9B9B" Offset="0"/>
<GradientStop Color="#FF666666" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="BackgroundBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFA21E00" Offset="0"/>
<GradientStop Color="#FF550000" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="MouseOverBackgroundBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFB63200" Offset="0"/>
<GradientStop Color="#FF681800" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="MouseDownBorderBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF666666" Offset="0"/>
<GradientStop Color="#FF9B9B9B" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="MouseDownBackgroundBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF550000" Offset="0"/>
<GradientStop Color="#FFA21E00" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ForegroundBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="#FFB9B9B9" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="MouseDownForegroundBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFB9B9B9" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Button.Resources>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>
<Setter Property="Background" Value="{StaticResource BackgroundBrush}"/>
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{StaticResource ForegroundBrush}"/>
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Border" CornerRadius="4" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<ContentPresenter Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource MouseOverBackgroundBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{StaticResource MouseDownBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource MouseDownBorderBrush}" />
<Setter Property="Foreground" Value="{StaticResource MouseDownForegroundBrush}" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<TextBlock Text="Posts Processed:" HorizontalAlignment="Right" Height="16" VerticalAlignment="Top" Margin="13,0,0,0" />
<TextBlock Grid.Column="1" Name="tbPostsProcessed" Margin="3,0,13,0" Text="{Binding PostsProcessed}" HorizontalAlignment="Left"/>
Edit:
I've also just discovered the MouseOver trigger seems to work everywhere to the right of the X (button content). Very strange. IsPressed also seems to only work on the same pixels that MouseOver works on.
I encountered a situation in the application i was developing. I want to write a template for a checkbox where i should be able to disable only the tick mark box and keep the label enabled. Is this really possible. I came up with a template, but when i disable the checkbox both the label and the tick mark get disabled.
just have a look at this xaml
<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>
<Style x:Key="CheckBoxFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Rectangle Margin="15,0,0,0" Stroke="#60000000" StrokeThickness="1" StrokeDashArray="1 2"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- PressedBrush is used for Pressed in Button, Radio Button, CheckBox -->
<LinearGradientBrush x:Key="PressedBrush" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#BBB" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="0.1"/>
<GradientStop Color="#EEE" Offset="0.9"/>
<GradientStop Color="#FFF" Offset="1.0"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBorderBrush" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#444" Offset="0.0"/>
<GradientStop Color="#888" Offset="1.0"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="NormalBrush" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#EEE" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="GlyphBrush" Color="#444"/>
<LinearGradientBrush x:Key="NormalBorderBrush" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="MouseOverBrush" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</LinearGradientBrush>
<!-- Simple CheckBox -->
<Style x:Key="CheckBoxStyle1" TargetType="{x:Type CheckBox}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="FocusVisualStyle" Value="{DynamicResource CheckBoxFocusVisual}"/>
<Setter Property="Background" Value="{DynamicResource NormalBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<!-- BulletDecorator is used to provide baseline alignment between the checkmark and the Content -->
<BulletDecorator x:Name="test" Grid.Column="0">
<BulletDecorator.Bullet>
<Grid Width="13" Height="13">
<Border x:Name="Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"/>
<Path x:Name="CheckMark" Stroke="{DynamicResource GlyphBrush}"
StrokeThickness="2"
SnapsToDevicePixels="False" Data="M 0 0 L 13 13 M 0 13 L 13 0"/>
</Grid>
</BulletDecorator.Bullet>
</BulletDecorator>
<ContentPresenter Grid.Column="1"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="True"/>
</Grid>
<!-- This uses Visibility to hide and show the CheckMark on IsChecked -->
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="false">
<Setter Property="Visibility" Value="Collapsed" TargetName="CheckMark"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="{DynamicResource MouseOverBrush}" TargetName="Border"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" Value="{DynamicResource PressedBrush}" TargetName="Border"/>
<Setter Property="BorderBrush" Value="{DynamicResource PressedBorderBrush}" TargetName="Border"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" Value="gray" TargetName="Border"/>
<Setter Property="BorderBrush" Value="black" TargetName="Border"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<CheckBox HorizontalAlignment="Left"
Margin="0,5,0,0" Style="{DynamicResource CheckBoxStyle1}"
VerticalAlignment="Top" Width="182" Height="17"
Content="Disabled" IsEnabled="False" IsChecked="True" />
<CheckBox HorizontalAlignment="Left"
Margin="125,5,0,0" Style="{DynamicResource CheckBoxStyle1}"
VerticalAlignment="Top" Width="182" Height="17"
Content="Active" IsEnabled="true" />
</Grid>
I had the same problem and came up with a quite stupid solution:
<StackPanel Orientation="Vertical" >
<TextBlock Text="Set Global Edition" TextWrapping="Wrap" HorizontalAlignment="Center" />
<CheckBox x:Name="GlobalEdition" IsChecked="{Binding IsGlobal}" HorizontalAlignment="Center" />
</StackPanel>
Since the TextBlock & Checkbox are separated, changing the Checkbox state won't influence the text attached to it.
(If I remember well, a Textbox does not change its appearance if disabled, maybe you could try with a TextBox instead of a label)
I'm using an indeterminate progressbar in my application and I get this bad animation of a series of blocks running horizontally. Isn't there a better style available like vista or windows 7?
Your progress bar has a style that corresponds to your current windows theme. If you run your application on Windows 7 with an Aero theme your progress bar will look accordingly.
If you want it to look always the same (no matter what windows theme is selected) you will need to define your own style for the progress bar.
Here is the style from the Aero Normal Color theme:
<LinearGradientBrush x:Key="ProgressBarBorderBrush"
EndPoint="0,1"
StartPoint="0,0">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#B2B2B2"
Offset="0"/>
<GradientStop Color="#8C8C8C"
Offset="1"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarBackground"
EndPoint="1,0"
StartPoint="0,0">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#BABABA"
Offset="0"/>
<GradientStop Color="#C7C7C7"
Offset="0.5"/>
<GradientStop Color="#BABABA"
Offset="1"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarTopHighlight"
StartPoint="0,0"
EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#80FFFFFF"
Offset="0.05"/>
<GradientStop Color="#00FFFFFF"
Offset="0.25"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarGlassyHighlight"
StartPoint="0,0"
EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#50FFFFFF"
Offset="0.5385"/>
<GradientStop Color="#00FFFFFF"
Offset="0.5385"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorGlassyHighlight"
StartPoint="0,0"
EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#90FFFFFF"
Offset="0.5385"/>
<GradientStop Color="#00FFFFFF"
Offset="0.5385"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<RadialGradientBrush x:Key="ProgressBarIndicatorLightingEffectLeft"
RadiusX="1"
RadiusY="1"
RelativeTransform="1,0,0,1,0.5,0.5">
<RadialGradientBrush.GradientStops>
<GradientStop Color="#60FFFFC4"
Offset="0"/>
<GradientStop Color="#00FFFFC4"
Offset="1"/>
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorLightingEffect"
StartPoint="0,1"
EndPoint="0,0">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#60FFFFC4"
Offset="0"/>
<GradientStop Color="#00FFFFC4"
Offset="1"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<RadialGradientBrush x:Key="ProgressBarIndicatorLightingEffectRight"
RadiusX="1"
RadiusY="1"
RelativeTransform="1,0,0,1,-0.5,0.5">
<RadialGradientBrush.GradientStops>
<GradientStop Color="#60FFFFC4"
Offset="0"/>
<GradientStop Color="#00FFFFC4"
Offset="1"/>
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorDarkEdgeLeft"
StartPoint="0,0"
EndPoint="1,0">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#0C000000"
Offset="0"/>
<GradientStop Color="#20000000"
Offset="0.3"/>
<GradientStop Color="#00000000"
Offset="1"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorDarkEdgeRight"
StartPoint="0,0"
EndPoint="1,0">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#00000000"
Offset="0"/>
<GradientStop Color="#20000000"
Offset="0.7"/>
<GradientStop Color="#0C000000"
Offset="1"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorAnimatedFill"
StartPoint="0,0"
EndPoint="1,0">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#00FFFFFF"
Offset="0"/>
<GradientStop Color="#60FFFFFF"
Offset="0.4"/>
<GradientStop Color="#60FFFFFF"
Offset="0.6"/>
<GradientStop Color="#00FFFFFF"
Offset="1"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<Style x:Key="{x:Type ProgressBar}"
TargetType="{x:Type ProgressBar}">
<Setter Property="Foreground"
Value="#01D328"/>
<Setter Property="Background"
Value="{StaticResource ProgressBarBackground}"/>
<Setter Property="BorderBrush"
Value="{StaticResource ProgressBarBorderBrush}"/>
<Setter Property="BorderThickness"
Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<Grid Name="TemplateRoot"
SnapsToDevicePixels="true">
<Rectangle Fill="{TemplateBinding Background}"
RadiusX="2"
RadiusY="2"/>
<Border Background="{StaticResource ProgressBarGlassyHighlight}"
Margin="1"
CornerRadius="2"/>
<Border BorderBrush="#80FFFFFF"
Background="{StaticResource ProgressBarTopHighlight}"
BorderThickness="1,0,1,1"
Margin="1"/>
<Rectangle Name="PART_Track"
Margin="1"/>
<Decorator x:Name="PART_Indicator"
HorizontalAlignment="Left"
Margin="1">
<Grid Name="Foreground">
<Rectangle x:Name="Indicator"
Fill="{TemplateBinding Foreground}"/>
<Grid x:Name="Animation" ClipToBounds="true">
<Rectangle x:Name="PART_GlowRect" Width="100"
Fill="{StaticResource ProgressBarIndicatorAnimatedFill}"
Margin="-100,0,0,0"
HorizontalAlignment="Left">
</Rectangle>
</Grid>
<Grid x:Name="Overlay">
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="15"/>
<ColumnDefinition Width="0.1*"/>
<ColumnDefinition MaxWidth="15"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Rectangle x:Name="LeftDark"
Grid.RowSpan="2"
Fill="{StaticResource ProgressBarIndicatorDarkEdgeLeft}"
RadiusX="1"
RadiusY="1"
Margin="1,1,0,1"/>
<Rectangle x:Name="RightDark"
Grid.RowSpan="2"
Grid.Column="2"
RadiusX="1"
RadiusY="1"
Fill="{StaticResource ProgressBarIndicatorDarkEdgeRight}"
Margin="0,1,1,1"/>
<Rectangle x:Name="LeftLight"
Grid.Column="0"
Grid.Row="2"
Fill="{StaticResource ProgressBarIndicatorLightingEffectLeft}"/>
<Rectangle x:Name="CenterLight"
Grid.Column="1"
Grid.Row="2"
Fill="{StaticResource ProgressBarIndicatorLightingEffect}"/>
<Rectangle x:Name="RightLight"
Grid.Column="2"
Grid.Row="2"
Fill="{StaticResource ProgressBarIndicatorLightingEffectRight}"/>
<Border x:Name="Highlight1"
Grid.RowSpan="2"
Grid.ColumnSpan="3"
Background="{StaticResource ProgressBarIndicatorGlassyHighlight}"/>
<Border x:Name="Highlight2"
Grid.RowSpan="2"
Grid.ColumnSpan="3"
Background="{StaticResource ProgressBarTopHighlight}"/>
</Grid>
</Grid>
</Decorator>
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
CornerRadius="2"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Orientation"
Value="Vertical">
<Setter TargetName="TemplateRoot"
Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="-90"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsIndeterminate"
Value="true">
<Setter TargetName="LeftDark"
Property="Visibility"
Value="Collapsed"/>
<Setter TargetName="RightDark"
Property="Visibility"
Value="Collapsed"/>
<Setter TargetName="LeftLight"
Property="Visibility"
Value="Collapsed"/>
<Setter TargetName="CenterLight"
Property="Visibility"
Value="Collapsed"/>
<Setter TargetName="RightLight"
Property="Visibility"
Value="Collapsed"/>
<Setter TargetName="Indicator"
Property="Visibility"
Value="Collapsed"/>
</Trigger>
<Trigger Property="IsIndeterminate"
Value="false">
<Setter TargetName="Animation"
Property="Background"
Value="#80B5FFA9"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Put this code in the Themes/Generic.xaml file in your application and your progress bars will always have this style.
This is another one... very simple flat progress bar for IsInderminate mode-
<ControlTemplate x:Key="CustomProgressBar" TargetType="ProgressBar" >
<Grid Name="TemplateRoot" SnapsToDevicePixels="True">
<Rectangle RadiusX="2" RadiusY="2" Fill="Transparent" />
<Border CornerRadius="0,0,0,0" Margin="1,1,1,1">
<Border.Background>
<SolidColorBrush Color="Transparent"/>
</Border.Background>
</Border>
<Border BorderThickness="0,0,0,0" BorderBrush="Transparent" Margin="1,1,1,1">
<Border.Background>
<SolidColorBrush Color="Transparent"/>
</Border.Background>
</Border>
<Rectangle Name="PART_Track" Margin="1,1,1,1" />
<Decorator Name="PART_Indicator" Margin="1,1,1,1" HorizontalAlignment="Left">
<Grid Name="Foreground">
<Rectangle Fill="Transparent" Name="Indicator" />
<Grid Name="Animation" ClipToBounds="True">
<Border Name="PART_GlowRect" Width="100" Margin="0,0,0,0" HorizontalAlignment="Left" Background="LightBlue"/>
</Grid>
<Grid Name="Overlay">
</Grid>
</Grid>
</Decorator>
<Border BorderThickness="0" CornerRadius="0,0,0,0" BorderBrush="Transparent" />
</Grid>
</ControlTemplate>
This is a custom progress bar I made from Suneet's example, but with IsIndeterminate animation working in .NET 3.5 :
<Style x:Key="{x:Type ProgressBar}" TargetType="{x:Type ProgressBar}">
<Setter Property="Foreground" Value="#54bdcd"/>
<Setter Property="Background" Value="#000000"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<Grid Name="TemplateRoot" SnapsToDevicePixels="true">
<Rectangle Fill="{TemplateBinding Background}"/>
<Rectangle Name="PART_Track" Margin="0"/>
<Decorator x:Name="PART_Indicator" HorizontalAlignment="Left" Margin="0">
<Grid Name="Foreground">
<Rectangle Fill="{TemplateBinding Foreground}" Name="Indicator" />
<Grid Name="Animation" ClipToBounds="True">
<Border Name="PART_GlowRect" Margin="0,0,0,0" HorizontalAlignment="Left" Background="{TemplateBinding Foreground}"/>
</Grid>
<Grid Name="Overlay">
</Grid>
</Grid>
</Decorator>
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsIndeterminate" Value="true">
<Setter TargetName="Indicator" Property="Fill" Value="Transparent" />
<Setter TargetName="PART_GlowRect" Property="Width" Value="100" />
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation
Storyboard.TargetName="PART_GlowRect"
Storyboard.TargetProperty="Margin"
From="-50,0,0,0" To="400,0,0,0" Duration="0:0:2"
AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This is kinda a hack because I move the indeterminate bar back and forth by changing its marginleft value, but in my case it was affordable because my progress bar was a fixed width.
If you have a better idea, feel free to change the storyboard part.
This page helped me a lot.
Here is another one that supports normal and intermediate mode:
<Style TargetType="{x:Type ProgressBar}">
<Setter Property="Foreground" Value="#1BA1E2"/>
<Setter Property="Background" Value="#EEEEEE"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<Grid Name="TemplateRoot" SnapsToDevicePixels="true">
<Rectangle Fill="{TemplateBinding Background}"/>
<Rectangle Name="PART_Track" Margin="0"/>
<Decorator x:Name="PART_Indicator" HorizontalAlignment="Left" Margin="0">
<Grid Name="Foreground">
<Rectangle Fill="{TemplateBinding Foreground}" Name="Indicator" />
<Grid x:Name="Animation" ClipToBounds="true" Visibility="Hidden">
<Rectangle Fill="{TemplateBinding Background}" Name="HiderPre" Margin="0,0,50,0">
<Rectangle.RenderTransform>
<ScaleTransform x:Name="HiderPreTransform" ScaleX="0"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Fill="{TemplateBinding Background}" Name="HiderPost" RenderTransformOrigin="1, 0" Margin="50,0,0,0">
<Rectangle.RenderTransform>
<ScaleTransform x:Name="HiderPostTransform" ScaleX="1" />
</Rectangle.RenderTransform>
</Rectangle>
</Grid>
<Grid Name="Overlay">
</Grid>
</Grid>
</Decorator>
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsIndeterminate" Value="true">
<Setter TargetName="Animation" Property="Visibility" Value="Visible" />
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation
Storyboard.TargetName="HiderPreTransform"
Storyboard.TargetProperty="(ScaleTransform.ScaleX)"
To="1"
Duration="0:00:4" AutoReverse="True"/>
<DoubleAnimation
Storyboard.TargetName="HiderPostTransform"
Storyboard.TargetProperty="(ScaleTransform.ScaleX)"
To="0"
Duration="0:00:4" AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="Gray" />
</Trigger>
</Style.Triggers>
</Style>
The simplest approach I could find is based heavily on https://stackoverflow.com/a/19737148/3195477 but even more simplified.
This produces a simple flat look:
<ProgressBar>
<ProgressBar.Template>
<ControlTemplate TargetType="ProgressBar">
<Border
BorderBrush="Black"
BorderThickness="1"
Background="LightGray"
>
<Grid x:Name="PART_Track">
<Rectangle
x:Name="PART_Indicator"
HorizontalAlignment="Left"
Fill="Blue"
/>
</Grid>
</Border>
</ControlTemplate>
</ProgressBar.Template>
</ProgressBar>
The "trick" is to just minimally replace the named parts with very simple alternatives. You don't necessarily need complex gradients etc. to improve on the default appearance, although that's a matter of opinion.
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:
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.
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?
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:
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:
{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.
RTL, animations, weird cases are not supported.
This style is slower than using BulletChrome which is heavily optimized.
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?
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).
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>
I've seen the following thread which is related to my question:
WPF ComboBox: background color when disabled
The above deals with changing the Content Template for a ComboBox. I am working with WPF, am somewhat new to Styles and Templates, and I want to change the dull gray background color of a disabled TextBox to some other color. We use TextBoxes frequently in our application and we find the default color settings difficult to read.
I've crafted the following solution attempt. But of course, it does not work. Can someone give me an opinion on why?
Unfortunately for the TextBox control, it appears like it's not as simple as just adding a trigger and changing the Background color when the trigger condition is true. You have to override the entire ControlTemplate to achieve this. Below is one example on how you might do this:
<Window x:Class="StackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="Red" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="White" />
<Style TargetType="TextBox">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border Name="Bd" BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="true">
<ScrollViewer Name="PART_ContentHost" Background="{TemplateBinding Background}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Value="{StaticResource DisabledBackgroundBrush}" Property="Background" />
<Setter Value="{StaticResource DisabledForegroundBrush}" Property="Foreground" />
<Setter TargetName="PART_ContentHost" Property="Background" Value="Blue"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Canvas>
<TextBox Text="TextBox" IsEnabled="False"/>
<TextBox Text="TextBox" IsEnabled="True" Canvas.Top="25"/>
</Canvas>
</Window>
EDIT:
In response to your question, I tried adding the ComboBox style to my original answer above and I was able to integrate it without errors. I'm not sure though if it behaves like you wanted it to. I just copy-pasted what's in the link you specified.
<Window x:Class="StackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StackOverflow"
Title="MainWindow" Height="350" Width="525"
x:Name="window">
<Window.Resources>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="Red" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="Blue" />
<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="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" />
<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="ToggleButton">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Border
x:Name="Border"
Grid.ColumnSpan="2"
CornerRadius="2"
Background="{StaticResource NormalBrush}"
BorderBrush="{StaticResource NormalBorderBrush}"
BorderThickness="1" />
<Border
Grid.Column="0"
CornerRadius="2,0,0,2"
Margin="1"
Background="{StaticResource WindowBackgroundBrush}"
BorderBrush="{StaticResource NormalBorderBrush}"
BorderThickness="0,0,1,0" />
<Path
x:Name="Arrow"
Grid.Column="1"
Fill="{StaticResource GlyphBrush}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 0 0 L 4 4 L 8 0 Z"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource DarkBrush}" />
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource PressedBrush}" />
</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}"/>
<Setter TargetName="Arrow" Property="Fill" Value="{StaticResource DisabledForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="ComboBoxTextBox" TargetType="TextBox">
<Border x:Name="PART_ContentHost" Focusable="False" Background="{TemplateBinding Background}" />
</ControlTemplate>
<Style x:Key="{x:Type ComboBox}" TargetType="ComboBox">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="MinWidth" Value="120"/>
<Setter Property="MinHeight" Value="20"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<ToggleButton
Name="ToggleButton"
Template="{StaticResource ComboBoxToggleButton}"
Grid.Column="2"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
</ToggleButton>
<ContentPresenter
Name="ContentSite"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Margin="3,3,23,3"
VerticalAlignment="Center"
HorizontalAlignment="Left" />
<TextBox x:Name="PART_EditableTextBox"
Style="{x:Null}"
Template="{StaticResource ComboBoxTextBox}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="3,3,23,3"
Focusable="True"
Background="Transparent"
Visibility="Hidden"
IsReadOnly="{TemplateBinding IsReadOnly}"/>
<Popup
Name="Popup"
Placement="Bottom"
IsOpen="{TemplateBinding IsDropDownOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Slide">
<Grid
Name="DropDown"
SnapsToDevicePixels="True"
MinWidth="{TemplateBinding ActualWidth}"
MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border
x:Name="DropDownBorder"
Background="{StaticResource WindowBackgroundBrush}"
BorderThickness="1"
BorderBrush="{StaticResource SolidBorderBrush}"/>
<ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
<Trigger SourceName="Popup" Property="Popup.AllowsTransparency" Value="true">
<Setter TargetName="DropDownBorder" Property="CornerRadius" Value="4"/>
<Setter TargetName="DropDownBorder" Property="Margin" Value="0,2,0,0"/>
</Trigger>
<Trigger Property="IsEditable"
Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
<Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
</Style.Triggers>
</Style>
<Style TargetType="TextBox">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border Name="Bd" BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="true">
<ScrollViewer Name="PART_ContentHost" Background="{TemplateBinding Background}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Value="{StaticResource DisabledBackgroundBrush}" Property="Background" />
<Setter Value="{StaticResource DisabledForegroundBrush}" Property="Foreground" />
<Setter TargetName="PART_ContentHost" Property="Background" Value="{StaticResource DisabledBackgroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel>
<TextBox IsEnabled="False">TextBox</TextBox>
<ComboBox IsEnabled="False"/>
</StackPanel>
</Window>
You can use the below snippet :
Instead of checking for IsEnable property, use IsReadonly property of TextBox control.
<Style TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="LightSkyBlue" />
<Style.Triggers>
<Trigger Property="IsReadOnly" Value="True">
<Setter Property="Background" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
If You need to apply it for all the textbox controls, use the above code.
For specific textbox, just set the key and apply the style to that Textbox.
For this situation I like to set Focusable=false and set the background color to my desired value (in a data-bound trigger). This is maybe a little hacky, but so is rewriting the control template for the whole TextBox. An alternative to Focusable is IsReadyOnly, but that doesn't work for as many controls. It does ensure the caret disappears, though.
You never use the ControlTemplate you defined. Also, you want a Style, not (necessarily) a ControlTemplate.
I think you want something like the following:
<Canvas.Resources>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="Red" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="White" />
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}" />
<Setter Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
</Trigger>
</Style.Triggers>
</Style>
</Canvas.Resources>
If you look at the template of the textbox, you will notice that the template has a trigger for the IsEnabled property False, and sets it's border element "Bd" background color to SystemColors.ControlBrushKey.
If you override this color in a style, it will achieve what you want to do.
<Style TargetType="{x:Type TextBox}">
<Style.Resources>
<SolidColorBrush
x:Key="{x:Static SystemColors.ControlBrushKey}"
Color="{StaticResource MyNewTextBoxBackgroundColor}" />
</Style.Resources>
</Style>
Try to avoid redefining control templates where you can. They tend to add a lot of code overhead and can become difficult to maintain over time.
I would use the following code in the Loaded event:
ClassicBorderDecorator o = VisualTreeHelper.GetChild(this.textBox1, 0) as ClassicBorderDecorator;
if (o != null)
{
o.Background = new SolidColorBrush(Colors.Transparent);
}
By adding <Window.Resources> after <Window> and before <Grid> will make your text box behave like normal winforms textbox.
<Window x:Class="..." Height="330" Width="600" Loaded="Window_Loaded" WindowStartupLocation="CenterOwner">
<Window.Resources>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsReadOnly" Value="True">
<Setter Property="Background" Value="LightGray" />
</Trigger>
<Trigger Property="IsReadOnly" Value="False">
<Setter Property="Background" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
Code taken from following web page:
wpf: Selecting the Text in TextBox with IsReadOnly = true?
And style modified to match winforms. (Their appearance is enabled = false, not readonly = true)
And of course your textbox must have IsReadOnly="True" attribute set.