Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / WPF
Tip/Trick

Highlight dates on a WPF Calendar

Rate me:
Please Sign up or sign in to vote.
5.00/5 (9 votes)
20 Feb 2013CPOL2 min read 40.1K   2.4K   19   4
Highligting and marking indvidual dates in a WPF Calendar

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.   

XML
<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

C#
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.   

C#
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. 

C#
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. 

C#
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.  

C#
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. 

C#
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. 

C#
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.

C#
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)


Written By
Software Developer (Senior) DSR
Denmark Denmark
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHi! Pin
Bernd Riemke16-Jan-18 3:45
Bernd Riemke16-Jan-18 3:45 
BugVery Cool, but it has a little bug Pin
Gustavo Bolanho18-Nov-14 17:03
Gustavo Bolanho18-Nov-14 17:03 
Generalthank you very much !!! Pin
varloteaux1-Nov-13 23:32
varloteaux1-Nov-13 23:32 
GeneralMy vote of 5 Pin
Faruk Pasic15-Jul-13 13:58
Faruk Pasic15-Jul-13 13:58 

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

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