Click here to Skip to main content
Click here to Skip to main content

Highlight dates on a WPF Calendar

By , 20 Feb 2013
Rate this:
Please Sign up or sign in to vote.

Introduction 

I wanted to use a WPF Calendar and highlight and mark individual dates of interest. I searched the web, but couldn't find any working solutions. I found "RedLetterDays" from Microsoft: http://msdn.microsoft.com/en-us/magazine/dd882520.aspx#id0430067, but that only works with WPF Toolkit and it is not very configurable. This solution doesn't rely on modifying the calendar, instead it modifies the background.  

Using the code 

Using the code is quite easy and is done in four steps: 

  1. Setting up the Calendar 
  2. Setting up icons
  3. Setting dates 
  4. Updating background when changing date   

The XAML part is nothing more than a basic Calendar.   

<Grid> 
    <Calendar Name="Kalender"  HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
</Grid>

The CalendarBackground is declared in the class. The background class is initialized with a reference to the Calendar class. This is done to be able to access DisplayDate

private readonly CalenderBackground background;  
background = new CalenderBackground(Kalender); 

First you must configure the icons you whish to use in the background. AddOverlay is called with an ID and a filename for the image. The images are 21x16 pixels with transparent backgrounds. The spacing between the rows in the Calendar varies between 15 and 16 pixels.   

background.AddOverlay("circle", "circle.png");
background.AddOverlay("tjek", "tjek.png");
background.AddOverlay("cross", "cross.png");
background.AddOverlay("box", "box.png");
background.AddOverlay("gray", "gray.png");   

Next you can add dates and the ID for the image you want to show on that date. It is possible to add more than one icon to one date. The overlay is done semi transparent so the icons are visible even if stacked. 

background.AddDate(new DateTime(2013, 02, 20), "tjek"); 
background.AddDate(new DateTime(2013, 02, 17), "tjek");
background.AddDate(new DateTime(2013, 02, 12), "tjek");
background.AddDate(new DateTime(2013, 02, 13), "tjek");
background.AddDate(new DateTime(2013, 02, 14), "tjek"); 
background.AddDate(new DateTime(2013, 02, 15), "tjek");
background.AddDate(new DateTime(2013, 02, 15), "circle");
background.AddDate(new DateTime(2013, 03, 01), "circle");
background.AddDate(new DateTime(2013, 03, 02), "circle");
background.AddDate(new DateTime(2013, 02, 10), "cross"); 

If you want you can set an option to mark the weekends. 

background.grayoutweekends = "gray";     

Assign the output from the class as the background for the Calendar. Create a DisplayDateChanged eventhandler to handle the update of the background when changing dates in the Calendar.  

Kalender.Background = background.GetBackground();
// Update background when changing the shown month
Kalender.DisplayDateChanged += KalenderOnDisplayDateChanged;

private void KalenderOnDisplayDateChanged(object sender, CalendarDateChangedEventArgs calendarDateChangedEventArgs)
{
  Kalender.Background = background.GetBackground();
}

The real "magic" takes place in the  CalenderBackground class.

First I must calculate the first shown date (January 28. in the screenshot above). The code handles Monday and Sunday as first day of week. As I know on which weekday the first day of the month is, I am able to calculate which days is the first shown. 

DateTime displaydate = _calendar.DisplayDate;
var firstdayofmonth = new DateTime(displaydate.Year, displaydate.Month, 1);
var dayofweek = (int) firstdayofmonth.DayOfWeek;
if (dayofweek == 0) dayofweek = 7; // set sunday to day 7.
if (dayofweek == (int)_calendar.FirstDayOfWeek) dayofweek = 8; // show a whole week ahead
if (_calendar.FirstDayOfWeek == DayOfWeek.Sunday) dayofweek += 1;
DateTime firstdate = firstdayofmonth.AddDays(-((Double) dayofweek) + 1);  

I create a default background with shading behind the month/year. 

var rtBitmap = new RenderTargetBitmap( 178 /* PixelWidth */, 160 /* PixelHeight */, 
        96 /* DpiX */, 96 /* DpiY */, PixelFormats.Default);
var drawVisual = new DrawingVisual();
using (DrawingContext dc = drawVisual.RenderOpen())
{
  var backGroundBrush = new LinearGradientBrush();
  backGroundBrush.StartPoint = new Point(0.5, 0);
  backGroundBrush.EndPoint = new Point(0.5, 1);
  backGroundBrush.GradientStops.Add(new GradientStop((Color) ColorConverter.ConvertFromString  ("#FFE4EAF0"), 0.0));
  backGroundBrush.GradientStops.Add(new GradientStop((Color) ColorConverter.ConvertFromString ("#FFECF0F4"), 0.16));
  backGroundBrush.GradientStops.Add(new GradientStop((Color) ColorConverter.ConvertFromString  ("#FFFCFCFD"), 0.16));
  backGroundBrush.GradientStops.Add(new GradientStop((Color) ColorConverter.ConvertFromString  ("#FFFFFFFF"), 1));
  dc.DrawRectangle(backGroundBrush, null, new Rect(0, 0, rtBitmap.Width, rtBitmap.Height));
}
rtBitmap.Render(drawVisual);

The final and most important part iterates through the 7 columns and 6 rows of the Calendar. As I know the first shown date, I  can traverse the dates and when I have a match  in the "datelist" I can add the overlay at the calculated position. This is done drawing a rectangle with the content from the overlay.

using (DrawingContext dc = drawVisual.RenderOpen())
{
    for (int y = 0; y < 6; y++)
        for (int x = 0; x < 7; x++)
        {
           int xpos = x*21 + 17;
           int ypos = y*16 + 50;

           foreach (string overlayid in datelist.Where(c => c.date == firstdate).Select(c => c.overlay))
           {
               if (overlayid != null)
               {
                   overlay overlay = overlays.Where(c => c.id == overlayid).FirstOrDefault();
                   dc.DrawRectangle(overlay.Brush, null /* no pen */,
                                         new Rect(xpos, ypos, overlay.BitMap.Width, overlay.BitMap.Height));
               }
           }
           firstdate = firstdate.AddDays(1);
       }
}   
rtBitmap.Render(drawVisual);
var brush = new ImageBrush(rtBitmap); // create a brush using the BitMap
return brush;

Voila. An ease way to make a custom calendar. 

Future improvements

One nice feature to add would be a mouseover tooltip with an explanation on why the date is highlighted.

History

  • 1.0: Initial POC.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Lars Pehrsson

Denmark Denmark
No Biography provided

Comments and Discussions

 
Generalthank you very much !!! Pinmembervarloteaux1-Nov-13 23:32 
GeneralMy vote of 5 PinmemberFaruk Pasic15-Jul-13 13:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140421.2 | Last Updated 20 Feb 2013
Article Copyright 2013 by Lars Pehrsson
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid