Introduction
Let us take a ticket booking example which is best suited for disabling dates. When I try to book a ticket, I should not be able to book a journey ticket for past dates and the return ticket date should not precede the starting date for the journey ticket.
Styles for Calendar
Step 1 : If you use Expression Blend we can easily do the custom styles. The following code is generated from Expression blend and I made a few changes for my project requirements such as color and design.
Step 2 : Add the following code in the resource dictionary (styles.xaml).
Code
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
xmlns:primitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls">
<Style x:Key="myCalendarDayButtonStyle" TargetType="primitives:CalendarDayButton">
<Setter Property="Background" Value="Aqua"/>
<Setter Property="FontSize" Value="10"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="MinWidth" Value="4"/>
<Setter Property="MinHeight" Value="4"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="primitives:CalendarDayButton">
<Grid Name="path">
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="CommonStates">
<vsm:VisualStateGroup.Transitions>
<vsm:VisualTransition GeneratedDuration="0:0:0.1"/>
</vsm:VisualStateGroup.Transitions>
<vsm:VisualState x:Name="Normal"/>
<vsm:VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="Opacity" To=".5"/>
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="Opacity" To=".5"/>
</Storyboard>
</vsm:VisualState>|
<vsm:VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="Opacity" To="0"/>
<DoubleAnimation Duration="0" Storyboard.TargetName="Content" Storyboard.TargetProperty="Opacity" To=".35"/>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="SelectionStates">|
<vsm:VisualStateGroup.Transitions>
<vsm:VisualTransition GeneratedDuration="0"/>
</vsm:VisualStateGroup.Transitions>
<vsm:VisualState x:Name="Unselected"/>
<vsm:VisualState x:Name="Selected">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="SelectedBackground" Storyboard.TargetProperty="Opacity" To=".75"/>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="CalendarButtonFocusStates">
<vsm:VisualStateGroup.Transitions>
<vsm:VisualTransition GeneratedDuration="0"/>
</vsm:VisualStateGroup.Transitions>
<vsm:VisualState x:Name="CalendarButtonFocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="FocusVisual" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="CalendarButtonUnfocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="FocusVisual" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="ActiveStates">
<vsm:VisualStateGroup.Transitions>
<vsm:VisualTransition GeneratedDuration="0"/>
</vsm:VisualStateGroup.Transitions>
<vsm:VisualState x:Name="Active"/>
<vsm:VisualState x:Name="Inactive">
<Storyboard>
<ColorAnimation Duration="0" Storyboard.TargetName="ContentBrush" Storyboard.TargetProperty="Color" To="#FF777777"/>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="DayStates">
<vsm:VisualStateGroup.Transitions>
<vsm:VisualTransition GeneratedDuration="0"/>
</vsm:VisualStateGroup.Transitions>
<vsm:VisualState x:Name="RegularDay"/>
<vsm:VisualState x:Name="Today">
</vsm:VisualState>
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="BlackoutDayStates" >
<vsm:VisualStateGroup.Transitions>|
<vsm:VisualTransition GeneratedDuration="0"/>
</vsm:VisualStateGroup.Transitions>
<vsm:VisualState x:Name="NormalDay">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="BlackoutVisual" Storyboard.TargetProperty="Opacity" To="0"/>
<ColorAnimation Duration="0" Storyboard.TargetName="ContentBrush" Storyboard.TargetProperty="Color" To="Black"/>
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="BlackoutDay">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="BlackoutVisual" Storyboard.TargetProperty="Opacity" To=".1"/>
<ColorAnimation Duration="0" Storyboard.TargetName="ContentBrush" Storyboard.TargetProperty="Color" To="#ACACAC"/>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
<Rectangle x:Name="TodayBackground" Fill="#6DBDD1" RadiusX="1" RadiusY="1" Opacity="0"/>
<Rectangle x:Name="SelectedBackground" Fill="{TemplateBinding Background}" RadiusX="1" RadiusY="1" Opacity="0"/>
<Rectangle x:Name="Background" Fill="{TemplateBinding Background}" RadiusX="1" RadiusY="1" Opacity="0"/>
<ContentControl x:Name="Content" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="5,1,5,1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" FontSize="{TemplateBinding FontSize}" IsTabStop="False" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}">
<ContentControl.Foreground>
<SolidColorBrush x:Name="ContentBrush" Color="Black"/>
</ContentControl.Foreground>
</ContentControl>
<Rectangle x:Name="BlackoutVisual" Stroke="Transparent" StrokeThickness="0" Fill="#F0F0F1" />
<Rectangle x:Name="FocusVisual" Stroke="#FF6DBDD1" RadiusX="1" RadiusY="1" IsHitTestVisible="false" Visibility="Collapsed"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="myCalendarStyle" TargetType="controls:Calendar">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Background" >
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFD3DEE8" Offset="0"/>
<GradientStop Color="#FFD3DEE8" Offset="0.16"/>
<GradientStop Color="#FFFCFCFD" Offset="0.16"/>
<GradientStop Color="#FFFFFFFF" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" >
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFA3AEB9" Offset="0"/>
<GradientStop Color="#FF8399A9" Offset="0.375"/>
<GradientStop Color="#FF718597" Offset="0.375"/>
<GradientStop Color="#FF617584" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:Calendar">
<StackPanel x:Name="Root" HorizontalAlignment="Center">
<primitives:CalendarItem x:Name="CalendarItem" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="CalendarDayButtonStyle" Value="{StaticResource myCalendarDayButtonStyle}"/>
</Style>
</ResourceDictionary>
Step 3 : Now add this resource dictionary to app.xaml.
Code
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="BlackoutDatesSL.App"
xmlns:style="clr-namespace:BlackoutDatesSL">
<Application.Resources>
<ResourceDictionary Source="Styles.xaml"/>
</Application.Resources>
</Application>
Step 4 : Now add the calendar style to datepicker in MainPage.xaml.
Code
<UserControl x:Class="BlackoutDatesSL.MainPage"
xmlns:sdk="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodel="clr-namespace:BlackoutDatesSL"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<viewmodel:ViewModel x:Key="ViewModel"/>
</UserControl.Resources>
<Grid DataContext="{Binding Source={StaticResource ViewModel}}" Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="From Date:" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"/>
<TextBlock Text="To Date:" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center"/>
<sdk:DatePicker TabIndex="1" Grid.Row="0" Grid.Column="1" Width="100" Name="FromDate"
SelectedDateChanged="FromDate_SelectedDateChanged" CalendarStyle="{StaticResource myCalendarStyle}"
SelectedDate="{Binding FromDate,Mode=TwoWay}"/>
<sdk:DatePicker TabIndex="2" Grid.Row="1" Width="100" Grid.Column="1" Name="ToDate"
CalendarStyle="{StaticResource myCalendarStyle}" SelectedDate="{Binding ToDate,Mode=TwoWay}" />
</Grid>
</UserControl>\
Step 5 : Now we have to add the logic for blackout dates.
-
When the page is loading, we should not display past dates in the date picker and after selecting a date, we should disable dates in the ToDate datepicker that precede starting date. The following is the code which will disable dates until yesterday.
Code
FromDate.BlackoutDates.Add(new CalendarDateRange(new DateTime(), DateTime.Now.AddDays(-1)));
ToDate.BlackoutDates.Add(new CalendarDateRange(new DateTime(), DateTime.Now.AddDays(-1)));
And in the selected date changed event of FromDate:
private void FromDate_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
{
if (((ViewModel)DataContext).ToDate != null)
{
if (((ViewModel)DataContext).ToDate < FromDate.SelectedDate)
((ViewModel)DataContext).ToDate = null;
}
this.ToDate.BlackoutDates.Clear();
this.ToDate.BlackoutDates.Add(new CalendarDateRange(new DateTime(), ((DateTime)this.FromDate.SelectedDate).AddDays(-1)));
}
My complete xaml.cs looks like below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace BlackoutDatesSL
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
FromDate.BlackoutDates.Add(new CalendarDateRange(new DateTime(), DateTime.Now.AddDays(-1)));
ToDate.BlackoutDates.Add(new CalendarDateRange(new DateTime(), DateTime.Now.AddDays(-1)));
this.DataContext = LayoutRoot.DataContext;
}
private void FromDate_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
{
if (((ViewModel)DataContext).ToDate != null)
{
if (((ViewModel)DataContext).ToDate < FromDate.SelectedDate)
((ViewModel)DataContext).ToDate = null;
}
this.ToDate.BlackoutDates.Clear();
this.ToDate.BlackoutDates.Add(new CalendarDateRange(new DateTime(), ((DateTime)this.FromDate.SelectedDate).AddDays(-1)));
}
}
}
My ViewmodelBase and ViewModel:
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace BlackoutDatesSL
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
ViewModel.cs
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace BlackoutDatesSL
{
public class ViewModel : ViewModelBase
{
private DateTime? _fromDate;
public DateTime? FromDate
{
get
{
return _fromDate;
}
set
{
_fromDate = value;
NotifyPropertyChanged("FromDate");
}
}
private DateTime? _toDate;
public DateTime? ToDate
{
get
{
return _toDate;
}
set
{
_toDate = value;
NotifyPropertyChanged("ToDate");
}
}
}
}
Step 6 : Now run the application and play with the date controls. I have attached complete source code.
If you find the above article useful, please rate and share the documents with your friends.