Showing posts with label DatePicker. Show all posts
Showing posts with label DatePicker. Show all posts

Monday, January 14, 2013

WPF DatePicker Control Style

How to style a WPF Date Picker control?

The WPF controls were hierarchical controls and most likely implements what other controls were implemented (inherited the styles and functions) as follow through how the User Interface reacts based on the events made by the end-user in your application. Same of how does WPF DatePicker control was implemented.

For your MSDN reference, please visit this link.

Styling WPF DatePicker control is not easy perhaps a slight complex than what of other controls in WPF.


First, you need to know how the WPF DatePicker control implements its PARTS. Once you understand how WPF DatePicker control implemented its PART, you need to drill down and determine what kind of control the PART was used in the WPF DatePicker control.

As per referenced, the parts of WPF DatePicker control were follow (based from MSDN):

Part

Type
PART_RootFrameworkElement
PART_TextBoxDatePickerTextBox
PART_ButtonButton
PART_PopupPopup

So, we will start the main Style of DatePicker control (see below).

<Style x:Key="styleDatePicker" TargetType="{x:Type DatePicker}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type DatePicker}">
                <Grid x:Name="PART_Root">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <DatePickerTextBox x:Name="PART_TextBox"
                                       BorderBrush="{TemplateBinding BorderBrush}"
                                       BorderThickness="{TemplateBinding BorderThickness}"
                                       HorizontalContentAlignment="Stretch"
                                       Padding="{TemplateBinding Padding}"
                                       VerticalContentAlignment="Center"
                                       Visibility="Visible"
                                       Grid.Column="0">
                    </DatePickerTextBox>
                    <Button x:Name="PART_Button">
                        <Button.Style>
                            <Style TargetType="{x:Type Button}">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="{x:Type Button}">
                                            <!-- Do button template and triggers here -->
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </Button.Style>
                    </Button>
                    <Popup x:Name="PART_Popup" StaysOpen="False" AllowsTransparency="True" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Above is a simple Style that can be used as a template when designing the WPF DatePicker control. You'll notice that the PART_Root were used a as top level control of the template. In WPF we need to implement the actual orders of how the WPF DatePicker control implemented them.

PART_TextBox: It is a part of the WPF DatePicker control of type DatePickerTextBox that holds the input control (free text control) in your DatePicker.

PART_Button: It is a part of the WPF DatePicker control of type Button that holds the actual button control that user is clicked when he/she needs to expand/collapsed the Calendar of the WPF DatePicker control.

PART_Popup: It is a part of the WPF DatePicker control of type Popup that holds the container of the Calendar control of the WPF DatePicker control.

Implementing WPF Date Picker control PART Styles

Let's start with the the chronological order implementation of WPF DatePicker control PARTS:

1. WPF Date Picker Control TextBox

Since it is a TextBox control type, we need to do the TextBox style.

<Style x:Key="styleDatePickerTextBox" TargetType="{x:Type DatePickerTextBox}">
    <Setter Property="Background" Value="Transparent"></Setter>
    <Setter Property="BorderBrush" Value="Transparent"></Setter>
    <Setter Property="BorderThickness" Value="0"></Setter>
    <Setter Property="IsEnabled" Value="True"></Setter>
    <Setter Property="IsReadOnly" Value="False"></Setter>
</Style>

You will notice we had referenced the TargetType property to TextBox control type and simply set the other important properties I like to modify. You will notice that nothing is different but only to disable the editing by setting the IsReadOnly property to False.

2. WPF Date Picker Control Button

This is the section where the user is clicking to collapse/expand the Calendar control.

<Button x:Name="PART_Button" VerticalAlignment="Stretch">
    <Button.Style>
        <Style TargetType="{x:Type Button}">
            <Setter Property="OverridesDefaultStyle" Value="True"></Setter>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Image Name="buttonImage"
                               Height="Auto"
                               HorizontalAlignment="Center"
                               Margin="1"
                               Source="../images/datepicker_unselected.png"
                               Stretch="Fill"
                               VerticalAlignment="Stretch"
                               Width="{Binding Path=Height, RelativeSource={RelativeSource Mode=Self}}">
                        </Image>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="buttonImage"
                                        Property="Source"
                                        Value="../images/datepicker_selected.png"></Setter>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Button.Style>
</Button>

So what the above style is doing? Based on the implementation of the button template and how the triggers reacts on the events. It simply display the resource image "../images/datepicker_unselected.png" as a default value.

Once the user mouse hovered the image control, the implemented "Trigger" will be triggered to set the Source property value of the image to value "../images/datepicker_selected.png".

3. WPF Date Picker Control PopUp

This is the section where the Calendar control is being placed/contained.

<Popup x:Name="PART_Popup" StaysOpen="False" AllowsTransparency="True" />

The above style for Popup control is to only implements and change the StaysOpen and AllowsTransparency property on how it reacts on the user interaction against the control and how the transparency being applied in the rendering.

4. WPF Date Picker Control Calendar

In the Calendar control, we have three important style needs to implement (if you want to override the style of each item inside it).

CalendarButtonStyle: The style will be applied to the month/year button of the Calendar control.

CalendarDayButtonStyle: The style will be applied to the day's button of the the Calendar control.

CalendarItemStyle: The style will be applied to the actual Calendar control.

For your reference on how to style Calendar control, please visit WPF Calendar Style blog.

WPF Calendar Control Style

How to style a WPF Calendar control?

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_RootFrameworkElement
PART_HeaderButtonButton
PART_PreviousButtonButton
PART_NextButtonButton
DayTitleTemplateKey name of DataTemplate
PART_MonthViewGrid
PART_YearViewGrid

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}">
                        <Label>
                            <TextBlock Foreground="#F7F7F7"
                                       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">
                    <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">
                            <Button x:Name="PART_PreviousButton"
                                    DockPanel.Dock="Left"
                                    Width="25"
                                    Content="&lt;"
                                    Focusable="False"></Button>
                            <Button x:Name="PART_NextButton"
                                    DockPanel.Dock="Right"
                                    Width="25"
                                    Content="&gt;" Focusable="False">
                            </Button>
                            <Button x:Name="PART_HeaderButton"
                                    Width="135"
                                    MaxWidth="135">
                            </Button>
                        </DockPanel>
                        <Grid Grid.Column="0"
                              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"/>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

There you see the placement and how we design the PARTS of the WPF Calendar control with the use of the Grid control columns and rows (divisions). You will see PART_Root, PART_HeaderButton, PART_PreviousButton, PART_NextButton, DayTitleTemplate, PART_MonthView and PART_YearView.

So depends on how will you going to implement your own style on each PART, just simply create your own custom style and reference it in every PART control.

Styling the CalendarButton

The style and template of this object is very simple to implement as it is just a CalendarButton object (you can customized with the used of Grid with a TextBlock inside of it). So the choice is yours.

Sample style I created for this:

<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>
                    <Border x:Name="border" CornerRadius="3">
                        <TextBlock x:Name="block"
                                   Foreground="#3A3A3A"
                                   FontSize="12"
                                   Text="{TemplateBinding Content}"
                                   VerticalAlignment="Center"
                                   HorizontalAlignment="Center">
                        </TextBlock>
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

You can apply your trigger based on your desires. However, you can use the trigger below for your reference.

<ControlTemplate.Triggers>
    <Trigger Property="IsInactive" Value="True">
        <Setter TargetName="block"
                Property="Foreground"
                Value="{DynamicResource ResourceKey=brushForegroundDisabled}">
        </Setter>
        <Setter Property="IsEnabled"
                Value="False">
        </Setter>
    </Trigger>
    <Trigger Property="HasSelectedDays" Value="True">
        <Setter TargetName="border_today"
                Property="Visibility"
                Value="Visible">
        </Setter>
    </Trigger>
    <MultiTrigger>
        <MultiTrigger.Conditions>
            <Condition SourceName="border"
                       Property="IsMouseOver"
                       Value="True">
            </Condition>
            <Condition Property="IsInactive"
                       Value="False">
            </Condition>
        </MultiTrigger.Conditions>
        <MultiTrigger.Setters>
            <Setter TargetName="border"
                    Property="Background"
                    Value="#66CED3C4">
            </Setter>
        </MultiTrigger.Setters>
    </MultiTrigger>
</ControlTemplate.Triggers>

Just change the design of the colors based on your design.

Note: The brushForegroundDisabled is a custom style reference, so you need to implement your own.

Finished Style for CalenderButton:

<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>
                    <Border x:Name="border" CornerRadius="3">
                        <TextBlock x:Name="block"
                                   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>
                        <Setter Property="IsEnabled"
                                Value="False">
                        </Setter>
                    </Trigger>
                    <Trigger Property="HasSelectedDays" Value="True">
                        <Setter TargetName="border_today"
                                Property="Visibility"
                                Value="Visible">
                        </Setter>
                    </Trigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition SourceName="border"
                                       Property="IsMouseOver"
                                       Value="True">
                            </Condition>
                            <Condition Property="IsInactive"
                                       Value="False">
                            </Condition>
                        </MultiTrigger.Conditions>
                        <MultiTrigger.Setters>
                            <Setter TargetName="border"
                                    Property="Background"
                                    Value="#66CED3C4">
                            </Setter>
                        </MultiTrigger.Setters>
                    </MultiTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Styling the CalendarDayButton

Same with CalendarButton, you have to determine the what type of control the PART is and implement the simple style by overriding its template. See sample below for your reference.

Note: It is a CalendarDayButton object.

<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>
                    <Border x:Name="border_selected"
                            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>
                    </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>
        <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>
            <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>
                    <Border x:Name="border_selected"
                            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>
                    </Border>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsInactive" Value="True">
                        <Setter TargetName="block"
                                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>
                            <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>
            </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

Once you're done with your styling, simply apply the actual CalendarItem Style object in the Calendar control. Below is the way how to do it.

<Style x:Key="styleDatePickerCalendar" TargetType="{x:Type Calendar}">
    <Setter Property="CalendarButtonStyle"
            Value="{StaticResource ResourceKey=styleDatePickerCalendarButton}">
    </Setter>
    <Setter Property="CalendarDayButtonStyle"
            Value="{StaticResource ResourceKey=styleDatePickerCalendarDayButton}">
    </Setter>
    <Setter Property="CalendarItemStyle"
            Value="{StaticResource ResourceKey=styleDatePickerCalendarItem}">
    </Setter>
</Style>

And that's it, we just finish styling the WPF Calendar control.