Same as what we had discussed when designing the WPF Date Picker control. You must know how the WPF Control implement the hierarchical order of the child controls on its own template.
In the first place, you should understand how PARTS are being implemented on the template of the WPF Calendar control. After that, you need to determine what type of control the PART was used. Once you know it already, it is very simple to place the style for the specific PART control.
For your reference of WPF Calendar Control parts, please visit this link.
As per referenced, the parts of the WPF Calendar control were follow (based from MSDN):
Part | Type |
PART_Root | FrameworkElement |
PART_HeaderButton | Button |
PART_PreviousButton | Button |
PART_NextButton | Button |
DayTitleTemplate | Key name of DataTemplate |
PART_MonthView | Grid |
PART_YearView | Grid |
The parts listed above should be placed inside the template you will be overriding when styling the WPF Calendar control. Below are the basic template of how to customized the WPF Calendar control.
As the main important control item, the CalendarItem is the actual calendar object that host the actual PARTS of the WPF Calendar control.
<Style x:Name="styleDatePickerCalendarItem"TargetType="{x:Type CalendarItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CalendarItem}">
<ControlTemplate.Resources>
<DataTemplate x:Key="{x:Static CalendarItem.DayTitleTemplateResourceKey}">
<DataTemplate x:Key="{x:Static CalendarItem.DayTitleTemplateResourceKey}">
<Label>
<TextBlock Foreground="#F7F7F7"
FontSize="12"
HorizontalAlignment="Center"
Margin="2"
Text="{Binding}"
VerticalAlignment="Center">
</TextBlock>
FontSize="12"
HorizontalAlignment="Center"
Margin="2"
Text="{Binding}"
VerticalAlignment="Center">
</TextBlock>
</Label>
</DataTemplate>
</ControlTemplate.Resources>
<Border Background="{StaticResource ResourceKey=gradientBrushCalendarBackground}"
BorderThickness="1"
BorderBrush="{DynamicResource ResourceKey=brushBorderControl}"
Padding="3">
<Border Background="{StaticResource ResourceKey=gradientBrushCalendarBackground}"
BorderThickness="1"
BorderBrush="{DynamicResource ResourceKey=brushBorderControl}"
Padding="3">
<Grid Name="PART_Root">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<DockPanel Grid.Column="0"
Grid.Row="0"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
LastChildFill="True">
Grid.Row="0"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
LastChildFill="True">
<Button x:Name="PART_PreviousButton"
DockPanel.Dock="Left"
Width="25"
Content="<"
Focusable="False"></Button>
DockPanel.Dock="Left"
Width="25"
Content="<"
Focusable="False"></Button>
<Button x:Name="PART_NextButton"
DockPanel.Dock="Right"
Width="25"
Content=">" Focusable="False">
</Button>
DockPanel.Dock="Right"
Width="25"
Content=">" Focusable="False">
</Button>
<Button x:Name="PART_HeaderButton"
Width="135"
MaxWidth="135">
</Button>
Width="135"
MaxWidth="135">
</Button>
</DockPanel>
<Grid Grid.Column="0"
Grid.Row="1"
VerticalAlignment="Center"
HorizontalAlignment="Center">
Grid.Row="1"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<Grid x:Name="PART_MonthView">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
</Grid>
<Grid x:Name="PART_YearView">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
</Grid>
</Grid>
<Rectangle x:Name="PART_DisabledVisual"
Opacity="0"
Visibility="Collapsed"
Fill="#F5F5F5"/>
Opacity="0"
Visibility="Collapsed"
Fill="#F5F5F5"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="styleDatePickerCalendarButton" TargetType="{x:Type CalendarButton}">
<Setter Property="OverridesDefaultStyle" Value="True"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CalendarButton}">
<Grid x:Name="grid">
<Border x:Name="border_today"
CornerRadius="2"
Background="#FFFFFF"
BorderBrush="#9A9A9A"
BorderThickness="1"
Visibility="Collapsed"></Border>
CornerRadius="2"
Background="#FFFFFF"
BorderBrush="#9A9A9A"
BorderThickness="1"
Visibility="Collapsed"></Border>
<Border x:Name="border" CornerRadius="3">
<TextBlock x:Name="block"
Foreground="#3A3A3A"
FontSize="12"
Text="{TemplateBinding Content}"
VerticalAlignment="Center"
HorizontalAlignment="Center">
</TextBlock>
Foreground="#3A3A3A"
FontSize="12"
Text="{TemplateBinding Content}"
VerticalAlignment="Center"
HorizontalAlignment="Center">
</TextBlock>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate.Triggers>
<Trigger Property="IsInactive" Value="True">
<Setter TargetName="block"
Property="Foreground"
Value="{DynamicResource ResourceKey=brushForegroundDisabled}">
</Setter>
Property="Foreground"
Value="{DynamicResource ResourceKey=brushForegroundDisabled}">
</Setter>
<Setter Property="IsEnabled"
Value="False">
</Setter>
Value="False">
</Setter>
</Trigger>
<Trigger Property="HasSelectedDays" Value="True">
<Setter TargetName="border_today"
Property="Visibility"
Value="Visible">
</Setter>
Property="Visibility"
Value="Visible">
</Setter>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition SourceName="border"
Property="IsMouseOver"
Value="True">
</Condition>
Property="IsMouseOver"
Value="True">
</Condition>
<Condition Property="IsInactive"
Value="False">
</Condition>
Value="False">
</Condition>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter TargetName="border"
Property="Background"
Value="#66CED3C4">
</Setter>
Property="Background"
Value="#66CED3C4">
</Setter>
</MultiTrigger.Setters>
</MultiTrigger>
</ControlTemplate.Triggers>
<Style x:Key="styleDatePickerCalendarButton" TargetType="{x:Type CalendarButton}">
<Setter Property="OverridesDefaultStyle" Value="True"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CalendarButton}">
<Grid x:Name="grid">
<Border x:Name="border_today"
CornerRadius="2"
Background="#FFFFFF"
BorderBrush="#9A9A9A"
BorderThickness="1"
Visibility="Collapsed"></Border>
CornerRadius="2"
Background="#FFFFFF"
BorderBrush="#9A9A9A"
BorderThickness="1"
Visibility="Collapsed"></Border>
<Border x:Name="border" CornerRadius="3">
<TextBlock x:Name="block"
Foreground="#3A3A3A"
FontSize="12"
Text="{TemplateBinding Content}"
VerticalAlignment="Center"
HorizontalAlignment="Center">
</TextBlock>
Foreground="#3A3A3A"
FontSize="12"
Text="{TemplateBinding Content}"
VerticalAlignment="Center"
HorizontalAlignment="Center">
</TextBlock>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsInactive" Value="True">
<Setter TargetName="block"
Property="Foreground"
Value="{DynamicResource ResourceKey=brushForegroundDisabled}">
</Setter>
Property="Foreground"
Value="{DynamicResource ResourceKey=brushForegroundDisabled}">
</Setter>
<Setter Property="IsEnabled"
Value="False">
</Setter>
Value="False">
</Setter>
</Trigger>
<Trigger Property="HasSelectedDays" Value="True">
<Setter TargetName="border_today"
Property="Visibility"
Value="Visible">
</Setter>
Property="Visibility"
Value="Visible">
</Setter>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition SourceName="border"
Property="IsMouseOver"
Value="True">
</Condition>
Property="IsMouseOver"
Value="True">
</Condition>
<Condition Property="IsInactive"
Value="False">
</Condition>
Value="False">
</Condition>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter TargetName="border"
Property="Background"
Value="#66CED3C4">
</Setter>
Property="Background"
Value="#66CED3C4">
</Setter>
</MultiTrigger.Setters>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Styling the CalendarDayButton
<Style x:Key="styleDatePickerCalendarDayButton" TargetType="{x:Type CalendarDayButton}">
<Setter Property="OverridesDefaultStyle" Value="True"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CalendarDayButton}">
<Grid x:Name="grid">
<Border x:Name="border_today"
CornerRadius="2"
Background="#FFFFFF"
BorderBrush="#9A9A9A"
BorderThickness="1"
Visibility="Collapsed">
</Border>
CornerRadius="2"
Background="#FFFFFF"
BorderBrush="#9A9A9A"
BorderThickness="1"
Visibility="Collapsed">
</Border>
<Border x:Name="border_selected"
CornerRadius="2"
Background="#E7E7E7"
BorderBrush="#9A5A9A"
BorderThickness="1"
Visibility="Collapsed">
</Border>
CornerRadius="2"
Background="#E7E7E7"
BorderBrush="#9A5A9A"
BorderThickness="1"
Visibility="Collapsed">
</Border>
<Border x:Name="border" CornerRadius="3">
<TextBlock x:Name="block"
Foreground="#3A3A3A"
Margin="10,3,10,3"
Text="{TemplateBinding Content}"
VerticalAlignment="Center"
HorizontalAlignment="Center">
</TextBlock>
Foreground="#3A3A3A"
Margin="10,3,10,3"
Text="{TemplateBinding Content}"
VerticalAlignment="Center"
HorizontalAlignment="Center">
</TextBlock>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
See below the trigger that you can use for this style.
<ControlTemplate.Triggers>
<Trigger Property="IsInactive" Value="True">
<Setter TargetName="block"
Property="Foreground"
Value="{DynamicResource ResourceKey=brushForegroundDisabled}">
</Setter>
Property="Foreground"
Value="{DynamicResource ResourceKey=brushForegroundDisabled}">
</Setter>
<Setter Property="IsEnabled"
Value="False">
</Setter>
Value="False">
</Setter>
</Trigger>
<Trigger Property="IsToday" Value="True">
<Setter TargetName="border_today"
Property="Visibility"
Value="Visible">
</Setter>
Property="Visibility"
Value="Visible">
</Setter>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True"></Condition>
<Condition Property="IsToday" Value="False"></Condition>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter TargetName="border_selected" Property="Visibility" Value="Visible"></Setter>
</MultiTrigger.Setters>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition SourceName="border" Property="IsMouseOver" Value="True"></Condition>
<Condition Property="IsInactive" Value="False"></Condition>
<Condition Property="IsSelected" Value="False"></Condition>
<Condition Property="IsToday" Value="False"></Condition>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter TargetName="border" Property="Background" Value="{DynamicResource ResourceKey=brushBorderControlDisabled}"></Setter>
</MultiTrigger.Setters>
</MultiTrigger>
</ControlTemplate.Triggers>
The actual code combination for CalendarDayButton
<Style x:Key="styleDatePickerCalendarDayButton" TargetType="{x:Type CalendarDayButton}">
<Setter Property="OverridesDefaultStyle" Value="True"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CalendarDayButton}">
<Grid x:Name="grid">
<Border x:Name="border_today"
CornerRadius="2"
Background="#FFFFFF"
BorderBrush="#9A9A9A"
BorderThickness="1"
Visibility="Collapsed">
</Border>
CornerRadius="2"
Background="#FFFFFF"
BorderBrush="#9A9A9A"
BorderThickness="1"
Visibility="Collapsed">
</Border>
<Border x:Name="border_selected"
CornerRadius="2"
Background="#E7E7E7"
BorderBrush="#9A5A9A"
BorderThickness="1"
Visibility="Collapsed">
</Border>
CornerRadius="2"
Background="#E7E7E7"
BorderBrush="#9A5A9A"
BorderThickness="1"
Visibility="Collapsed">
</Border>
<Border x:Name="border" CornerRadius="3">
<TextBlock x:Name="block"
Foreground="#3A3A3A"
Margin="10,3,10,3"
Text="{TemplateBinding Content}"
VerticalAlignment="Center"
HorizontalAlignment="Center"></TextBlock>
Foreground="#3A3A3A"
Margin="10,3,10,3"
Text="{TemplateBinding Content}"
VerticalAlignment="Center"
HorizontalAlignment="Center"></TextBlock>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsInactive" Value="True">
<Setter TargetName="block"
Property="Foreground"
Value="{DynamicResource ResourceKey=brushForegroundDisabled}">
</Setter>
Property="Foreground"
Value="{DynamicResource ResourceKey=brushForegroundDisabled}">
</Setter>
<Setter Property="IsEnabled" Value="False"></Setter>
</Trigger>
<Trigger Property="IsToday" Value="True">
<Setter TargetName="border_today" Property="Visibility" Value="Visible"></Setter>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True"></Condition>
<Condition Property="IsToday" Value="False"></Condition>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter TargetName="border_selected" Property="Visibility" Value="Visible"></Setter>
</MultiTrigger.Setters>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition SourceName="border"
Property="IsMouseOver"
Value="True">
</Condition>
Property="IsMouseOver"
Value="True">
</Condition>
<Condition Property="IsInactive"
Value="False">
</Condition>
Value="False">
</Condition>
<Condition Property="IsSelected"
Value="False">
</Condition>
Value="False">
</Condition>
<Condition Property="IsToday"
Value="False">
</Condition>
Value="False">
</Condition>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter TargetName="border"
Property="Background"
Value="{DynamicResource ResourceKey=brushBorderControlDisabled}">
</Setter>
Property="Background"
Value="{DynamicResource ResourceKey=brushBorderControlDisabled}">
</Setter>
</MultiTrigger.Setters>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Please note that other custom style like brushForegroundDisabled and brushBorderControlDisabled is just a custom style that you can modify base on your own desire.
So be on this implementation to simplify everything. ;)
Applying in Calendar
<Style x:Key="styleDatePickerCalendar" TargetType="{x:Type Calendar}">
<Setter Property="CalendarButtonStyle"
Value="{StaticResource ResourceKey=styleDatePickerCalendarButton}">
</Setter>
<Setter Property="CalendarDayButtonStyle"
Value="{StaticResource ResourceKey=styleDatePickerCalendarDayButton}">
</Setter>
Value="{StaticResource ResourceKey=styleDatePickerCalendarDayButton}">
</Setter>
<Setter Property="CalendarItemStyle"
Value="{StaticResource ResourceKey=styleDatePickerCalendarItem}">
</Setter>
Value="{StaticResource ResourceKey=styleDatePickerCalendarItem}">
</Setter>
</Style>
And that's it, we just finish styling the WPF Calendar control.
And that's it, we just finish styling the WPF Calendar control.
Thanks for the posting as I am working on a project that does this exactly. The curious part is my resource.xaml that controls all of this and it isn't an exact copy of your but close. My custom DatePicker style works no problem but when I try to customize the calendar control none of my changes take affect. How does the Custom DatePicker know to use the custom Calendar style?
ReplyDeleteI have my
<DatePicker Style="{DynamicResource DatePickerStyle}" Height="25" HorizontalAlignment="Center" Name="DatePickerLeft"
in my xaml file and it controls the customer datepicker no problem. Any help would be greatly appreciated.
Thanks
Chris
Hi Chris, I was just very busy last week so I missed replying in your comment immediately. Anyway...
DeleteBut the best thing you can do is to override the style of you DatePicker, you can see it here (http://codesdirectory.blogspot.com/2013/01/wpf-datepicker-control-style.html).
However, if you don't want to override it, you can use the CalendarStyle property to apply the custom style you created for your Calendar control. See below the application.
Hey.
ReplyDeleteAwesome guide. I have tried copy in your code and mix it with the guide you wrote for the DatePicker control.
My problem is, the calender does not refresh when "going out" from day overview to month to years and so on.
So when im in month overview, i can still see the date numbers.
You know how to fix it?
-Kristian
Hi,
ReplyDeleteI also have the same issue as Kristian Above. I am not able to get it to refresh when it switches from month to year view
Hi,
ReplyDeleteI 've solved the problem adding Triggers :
It works good in my project.
Hope it will be usefull
Style x:Name="styleDatePickerCalendarItem"TargetType="{x:Type CalendarItem}"
ReplyDelete^^ Please add space between style name and target type, also replace x:Name with x:Key, otherwise it won't compile.