Highlighting Dates on a WPF Calendar Control


Any time I’m using GUI frameworks, I run into a control that I thought for sure the developers would have put a little more thought into. We’ve all experienced it, that one control that we just can’t customize as much as we would like. In my world, for WinForms, it was the hideous TabControl. For WPF, it’s the Calendar control.

The Problem

The Calendar control is beautiful and performant, but highly inflexible. I searched the Internet for days looking for a way to highlight (not select!) significant dates. There were plenty of workarounds, all of which took far too many lines of codes for such a simple task. I also looked into alternative libraries, which possibly may have been more flexible but were unsightly, Windows 95-looking flops (see: Syncfusion CalendarEdit). In a last-ditch effort I thought I’d roll my own; just override the control and add the functionality myself. However, thanks to everything being marked private or internal there are some functions within the control I don’t have access to outside of Microsoft’s assembly. Okay, so change it! After all, Microsoft publishes its Reference Source. Oh wait, that’s right; it’s reference source, so it would be illegal to modify and redistribute a part of the .NET framework that hasn’t been open-sourced yet.

At this point I would normally give up or switch frameworks. I’ve been on a Qt binge lately. Thankfully I didn’t, and this post documents my approach.

The Solution

Like most complex controls, the Calendar control is a collection of simpler controls, in this case the CalendarDayButton control. Microsoft could have made it easy and just given us a getter for the days, but for some reason decided not to (or overlooked this entirely), which doesn’t make much sense to me as the CalendarDayButton class is even thoroughly documented, so why not make it easy for us to use it in standard controls?! Regardless, the first task is to somehow gain access to those day classes from the parent calendar.

Well, being controls, they have events; and XAML for whatever reason seems to have access to those events that my code-behind doesn’t. Since every control (that I know of) has a Loaded event, we can hook up an event handler for every CalendarDayButton that gets created.

<Calendar x:Name="calendar">
    <Calendar.CalendarDayButtonStyle>
        <Style TargetType="CalendarDayButton" BasedOn="{StaticResource {x:Type CalendarDayButton}}">
            <EventSetter Event="Loaded" Handler="calendarButton_Loaded" />
        </Style>
    </Calendar.CalendarDayButtonStyle>
</Calendar>

In the calendarButton_Loaded event we can finally get access to that pesky, elusive CalendarDayButton object via the sender. We can also obtain the date the CalendarDayButton refers to. According to the Reference Source, the DateTime object that represents the date of the button is assigned to the button’s DataContext. Using this, we can determine if we’re highlighting the date. In this example I create a collection of “significant dates” of type List<DateTime> in the window’s Loaded event and search it, determining which color to make the button.

private void calendarButton_Loaded(object sender, EventArgs e)
{
    CalendarDayButton button = (CalendarDayButton)sender;
    DateTime date = (DateTime)button.DataContext;
    HighlightDay(button, date);
    button.DataContextChanged += new DependencyPropertyChangedEventHandler(calendarButton_DataContextChanged);
}

private void HighlightDay(CalendarDayButton button, DateTime date)
{
    if (significantDates.Contains(date))
        button.Background = Brushes.LightBlue;
    else
        button.Background = Brushes.White;
}

Notice above that I also attach a handler to the button’s DataContextChanged event. This will allow us to update the highlighting when the user changes months (which makes all of the buttons’ dates change, rendering the highlighting incorrect). We need this since the Loaded event never gets called again.

private void calendarButton_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    CalendarDayButton button = (CalendarDayButton)sender;
    DateTime date = (DateTime)button.DataContext;
    HighlightDay(button, date);
}

And that’s it, enjoy your snazzy new highlighted calendar!

C# Highlighted Calendar

Things get a little complicated when combining this with the built-in highlighting for the current day (as you can see, today, November 6, is missing because the text is white), but I’ll leave that to you to figure out if you need to; maybe even leave a comment describing how you did it. 😉 Feel free to view or download the full source below.


One response on “Highlighting Dates on a WPF Calendar Control

  1. Instead of making the background white, you can make it null:

    if (significantDates.Contains(date))
    {
    button.Background = Brushes.PaleVioletRed;
    button.ToolTip = “Missing timesheet record!”;
    }
    else
    {
    button.Background = null;
    button.ToolTip = null;
    }

Leave a Reply