Reputation: 303
I'm using WPF Calendar control to add Holidays. User can select days and the selected days will be inserted to a database table as holiday.
After the insertion, I'm displaying the days in Holiday table as BlackoutDates
in the calendar as in the following code.
XAML
<Calendar x:Name="HolidayCalendar" SelectedDatesChanged="Calendar_SelectionChanged"
SelectionMode="MultipleRange" Loaded="Calendar_Loaded">
C#
DateTime now = DateTime.Now;
var startDate = new DateTime(now.Year, now.Month, 1);
var endDate = startDate.AddMonths(1).AddDays(-1);
GetHolidayService getHolidayService = new GetHolidayService();
ObservableCollection<GetHoliday> GetHolidayBaseList = new ObservableCollection<GetHoliday>();
PostHoliday postHoliday = new PostHoliday();
postHoliday.StartDate = startDate;
postHoliday.EndDate = endDate;
GetHolidayBaseList = getHolidayService.GetHolidayServiceFunction(postHoliday);
foreach (var item in GetHolidayList)
{
HolidayCalendar.BlackoutDates.Add(new CalendarDateRange(item.HolidayDate,item.HolidayDate)) ;
}
Current output
I want to achieve something like this - Holidays marked as Red
as in this link
Any help to write a control template required for this functionality is appreciated.
Thanks in advance!
Upvotes: 1
Views: 2757
Reputation: 693
My solution is pure XAML, using Visual states :
<Style TargetType="CalendarDayButton" x:Key="CalendarDayButtonStyle">
<Setter Property="MinWidth" Value="5" />
<Setter Property="MinHeight" Value="5" />
<Setter Property="FontSize" Value="14" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CalendarDayButton">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.1" />
</VisualStateGroup.Transitions>
<VisualState Name="Normal" />
<VisualState Name="MouseOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="HighlightBackground" Storyboard.TargetProperty="Opacity" To="1" Duration="0" />
</Storyboard>
</VisualState>
<VisualState Name="Pressed">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="HighlightBackground" Storyboard.TargetProperty="Opacity" To="1" Duration="0" />
</Storyboard>
</VisualState>
<VisualState Name="Disabled">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="HighlightBackground" Storyboard.TargetProperty="Opacity" To="0" Duration="0" />
<DoubleAnimation Storyboard.TargetName="NormalText" Storyboard.TargetProperty="Opacity" To=".35" Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup Name="SelectionStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0" />
</VisualStateGroup.Transitions>
<VisualState Name="Unselected" />
<VisualState Name="Selected">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="SelectedBackground" Storyboard.TargetProperty="Opacity" To="1" Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup Name="ActiveStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0" />
</VisualStateGroup.Transitions>
<VisualState Name="Active" />
<VisualState Name="Inactive">
<Storyboard>
<ColorAnimation Duration="0" Storyboard.TargetName="NormalText" Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)"
To="#FFCBCBCB" />
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup Name="DayStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0" />
</VisualStateGroup.Transitions>
<VisualState Name="RegularDay" />
<VisualState Name="Today">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="TodayBackground" Storyboard.TargetProperty="Opacity" To="1" Duration="0" />
<ColorAnimation Duration="0" Storyboard.TargetName="NormalText" Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)"
To="#FFFFFFFF" />
</Storyboard>
</VisualState>
<VisualState Name="BlackoutDay">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="HighlightBackground" Storyboard.TargetProperty="Opacity" To="0" Duration="0" />
<DoubleAnimation Storyboard.TargetName="NormalText" Storyboard.TargetProperty="Opacity" To=".35" Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="TodayBackground" RadiusX="1" RadiusY="1" Opacity="0" Fill="Blue"/>
<Rectangle x:Name="SelectedBackground" RadiusX="1" RadiusY="1" Opacity="0" Fill="Silver"/>
<Rectangle x:Name="HighlightBackground" RadiusX="1" RadiusY="1" Opacity="0" Fill="LightGray"/>
<ContentPresenter x:Name="NormalText" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="5,1,5,1" TextElement.Foreground="Black"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Solution lies in defining own ControlTemplate with It's visual states. Most important thing is inside VisualStateGroup "DayStates" - VisualState with name "BlackoutDay".
This way no attached properties is needed for overriding default blackout style, and all sorts of button colors for each control's state can be completely customized.
Upvotes: 0
Reputation: 6322
This is how you can do it,
First define custom style for CalendarDataButton based on standard style
<Calendar x:Name="HolidayCalendar" SelectionMode="MultipleRange">
<Calendar.CalendarDayButtonStyle>
<Style TargetType="CalendarDayButton" BasedOn="{StaticResource {x:Type CalendarDayButton}}">
<Style.Triggers>
<Trigger Property="IsBlackedOut" Value="True">
<Setter Property="Background" Value="DeepPink"/>
<Setter Property="local:CalenderHelper.IsBlackOutDisabled" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
</Calendar.CalendarDayButtonStyle>
<Calendar.BlackoutDates>
<CalendarDateRange Start="21-Nov-2018" End="21-Nov-2018"/>
<CalendarDateRange Start="25-Nov-2018" End="25-Nov-2018"/>
</Calendar.BlackoutDates>
</Calendar>
In order to remove the strike mark from Blackout date, you will have to use the below attached property 'IsBlackOutDisabled'
public class CalenderHelper : DependencyObject
{
public static readonly DependencyProperty IsBlackOutDisabledProperty =
DependencyProperty.RegisterAttached("IsBlackOutDisabled", typeof(bool), typeof(CalenderHelper), new PropertyMetadata(false, OnIsBlackOutDisabledChanged));
public static bool GetIsBlackOutDisabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsBlackOutDisabledProperty);
}
public static void SetIsBlackOutDisabled(DependencyObject obj, bool value)
{
obj.SetValue(IsBlackOutDisabledProperty, value);
}
private static void OnIsBlackOutDisabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CalendarDayButton dayButton = d as CalendarDayButton;
if (dayButton.IsLoaded)
{
SetBlackout(dayButton, (bool)e.NewValue);
}
else
{
dayButton.Loaded += (s, ee) =>
{
SetBlackout(dayButton, (bool)e.NewValue);
};
}
}
static void SetBlackout(CalendarDayButton dayButton, bool collapsed)
{
ControlTemplate template = dayButton.Template;
Path blackoutPath = template.FindName("Blackout", dayButton) as Path;
if (collapsed)
blackoutPath.Visibility = System.Windows.Visibility.Collapsed;
else
blackoutPath.Visibility = System.Windows.Visibility.Visible;
}
}
UPDATE:
If you want the colored dates to be selectable by Calendar, you must go with different approach. Blackout Dates will not work for you.
This approach takes the converter to color the dates.
First define custom style for CalendarDataButton based on standard style
<Window.Resources>
<local:CustomLetterDayConverter x:Key="CustomLetterDayConverter" />
<Style x:Key="CalendarDayButtonStyle" TargetType="{x:Type CalendarDayButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={StaticResource CustomLetterDayConverter}}" Value="{x:Null}">
<Setter Property="Background" Value="HotPink"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Calendar x:Name="HolidayCalendar" SelectionMode="MultipleRange" SelectedDate="{Binding SelectedDate}"
CalendarDayButtonStyle="{StaticResource CalendarDayButtonStyle}"
>
</Calendar>
</Grid>
The custom converter
public class CustomLetterDayConverter : IValueConverter
{
static HashSet<DateTime> dict = new HashSet<DateTime>();
static CustomLetterDayConverter()
{
dict.Add(DateTime.Today);
dict.Add(DateTime.Today.AddDays(1));
dict.Add(DateTime.Today.AddDays(2));
dict.Add(DateTime.Today.AddDays(5));
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string text = null;
if (dict.Contains((DateTime)value))
text = null;
else
text = "";
return text;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Upvotes: 2