Click here to Skip to main content
15,897,519 members
Articles / Desktop Programming / WPF

Extending the WPF Calendar Control

Rate me:
Please Sign up or sign in to vote.
4.84/5 (28 votes)
10 Sep 2010CPOL26 min read 156K   5K   65  
This article shows how to create a custom control that extends an existing WPF control. It extends the WPF Calendar control by adding date highlighting.
using System;

namespace FsCalendarDemo
{
	public class MainWindowViewModel : ViewModelBase
	{
		#region Fields

		// Property variables
		private DateTime p_DisplayDate;
		private string[] p_HighlightedDateText;

		// Member variables
		private DateTime m_OldDisplayDate;

		#endregion

		#region Constructor

		public MainWindowViewModel()
		{
			this.Initialize();
		}

		#endregion

		#region Events

		/// <summary>
		/// Notifies subscriubers that a UI refresh has been requested.
		/// </summary>
		/// <remarks>
		/// We use this event because this view model has no knowledge of the view that
		/// uses it. That keeps the view model from being dependent on the view. But it
		/// means that this view model can't directly invoke a refresh method on the view.
		/// So, it raises this event, and the view subscribes to it. The view invokes any
		/// refresh method that may be implemented there.
		/// </remarks>
		public event EventHandler RefreshRequested;

		#endregion


		#region Properties

		/// <summary>
		/// The DisplayDate in the calendar.
		/// </summary>
		public DateTime DisplayDate
		{
			get { return p_DisplayDate; }

			set
			{
				base.RaisePropertyChangingEvent("DisplayDate");
				p_DisplayDate = value;
				base.RaisePropertyChangedEvent("DisplayDate");
			}
		}

		/// <summary>
		/// The text to be shown in tool tips for highlighted dates.
		/// </summary>
		public string[] HighlightedDateText
		{
			get { return p_HighlightedDateText; }

			set
			{
				base.RaisePropertyChangingEvent("HighlightedDateText");
				p_HighlightedDateText = value;
				base.RaisePropertyChangedEvent("HighlightedDateText");
			}
		}

		#endregion

		#region Event Handlers

		/// <summary>
		/// Updates the HighlightedDateText property when the calendar is changed to a different month.
		/// </summary>
		private void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
		{
			// Ignore properties other than DisplayDate
			if (e.PropertyName != "DisplayDate") return;

			// Ignore change if date is DateTime.MinValue
			if (p_DisplayDate == DateTime.MinValue) return;

			// Ignore change if month is the same
			if (p_DisplayDate.IsSameMonthAs(m_OldDisplayDate)) return;

			// Populate month
			this.SetMonthHighlighting();

			// Update OldDisplayDate
			m_OldDisplayDate = p_DisplayDate;
		}

		#endregion

		#region Private Methods

		/// <summary>
		/// Initializes the view model.
		/// </summary>
		private void Initialize()
		{
			// Initialize highlight text array
			p_HighlightedDateText = new String[31];

			// Set the display date to today
			p_DisplayDate = DateTime.Today;

			// Set month highlighting
			this.SetMonthHighlighting();

			// Subscribe to PropertyChanged event
			this.PropertyChanged += OnPropertyChanged;
		}

		/// <summary>
		/// Requests a Refresh from the view.
		/// </summary>
		private void RequestRefresh()
		{
			if (this.RefreshRequested != null)
			{
				this.RefreshRequested(this, new EventArgs());
			}
		}

		/// <summary>
		/// Sets highlighting for a month.
		/// </summary>
		private void SetMonthHighlighting()
		{
			var displayMonth = this.DisplayDate.Month;
			var displayYear = this.DisplayDate.Year;

			// Get the last day of the display month
			var month = this.DisplayDate.Month;
			var year = this.DisplayDate.Year;
			var lastDayOfMonth = DateTime.DaysInMonth(year, month);

			// Set the highlighted date text
			for (var i = 0; i < 31; i++)
			{
				// First set this array element to null
				p_HighlightedDateText[i] = null;

				/* This demo simply highlights odd dates. So, if the array element represents 
				 * an even date, we leave the element at its null setting and skip to the next 
				 * increment of the loop. Note that the array is indexed from zero, while a 
				 * calendar is indexed from one. That means odd-numbered elements represent 
				 * even-numbered dates. So, if the index is odd, we skip.  */

				// If index is odd, skip to next
				if (i%2 == 1) continue;

				/* An element may be out of range for the current month. For example, element
				 * 30 would represent the 31st, which would be out of range for a month that 
				 * has only 30 days. If that's the case for the current element, we leave it
				 * set to null and skip to the next increment of the loop. */

				// If element is out of range, skip to next
				if (i >= lastDayOfMonth) continue;

				/* Since the array is indexed from zero, and a calendar is indexed from one, 
				 * we have to add one to the array index to get the calendar day to which it 
				 * corresponds. All we do in this demo is put the Long Date String is the
				 * HighlightedDateText array. */

				// Set highlight date text
				var targetDate = new DateTime(displayYear, displayMonth, i + 1);
				p_HighlightedDateText[i] = targetDate.ToLongDateString();
			}

			// Refresh the calendar
			this.RequestRefresh();
		}

		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

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) Foresight Systems
United States United States
David Veeneman is a financial planner and software developer. He is the author of "The Fortune in Your Future" (McGraw-Hill 1998). His company, Foresight Systems, develops planning and financial software.

Comments and Discussions