Click here to Skip to main content
Click here to Skip to main content
Go to top

Quick and Simple WPF Month-view Calendar - Updated

, 21 Aug 2009
Rate this:
Please Sign up or sign in to vote.
A very simple, XAML-based month-view calendar that shows appointments, exposes events, and allows dragging appointments in the current month.

Introduction 

This is a slightly updated version of the "Quick and Simple WPF Month-view Calendar" that I posted 11-Mar-2009 (I would have made this a new version of that project, but I'm apparently too dense to figure out how to do that. Doh!). Other than using binding to set the appointment text and tag value, the only significant change is that you can now drag appointments between days on the currently-visible month. The move raises a new event, "AppointmentMoved". 

From the original introduction I wrote in March: While working on a freelance project in February, I needed a basic month-view calendar control to use in a few places in the app. I searched, and found Creating an Outlook Calendar using WPF (by rudigobbler), which was a great day-view. I also found Richard Gavel's excellent on-going (I hope) work on Creating the Outlook UI with WPF, a 7-part series (there may be other WPF calendar controls out there now as well). Both of those are more serious efforts to create real, reusable components for other developers - but neither had a month-view. I was also in a time-crunch, and didn't want to pay $800+ for a suite of WPF tools from one of the vendors. Since I really needed just very basic functionality, I took an afternoon and wrote this one.

Update: I spent another half-day implementing the drag feature - part of this was shoehorning a much-cut-down C# DragDropHelper class originally posted by Bea Stollnitz on her excellent blog into a "lite" VB.NET class to use in the sample code.

Background

This article assumes you have a basic understanding of .NET and WPF. The code is not complicated, nor is it long. I make use of lambda functions (System.Predicate) in order to find appointments for each day, but that's about as complicated as it gets. Note that this requires .NET Framework 3.x - I built it on 3.5 SP1. 

Using the Code

The sample app (see download at top) shows the basic use of the calendar. Note that in Window1.xaml, I only include a reference to the local assembly, like so:  

xmlns:cal="clr-namespace:QuickWPFMonthCalendar"

Window1.xaml only contains one line of markup between the Window open and closing tags:

<cal:MonthView x:Name="AptCalendar" VerticalAlignment="Stretch" 
               VerticalContentAlignment="Stretch"/> 

This is Window1.xaml in Visual Studio 2008's design mode. As you can see, using it is pretty simple:

The MonthView control exposes several events, which are declared in MonthView.xaml.vb like this (note that AppointmentMoved is a new event for this version):

Public Event DisplayMonthChanged(ByVal e As MonthChangedEventArgs)
Public Event DayBoxDoubleClicked(ByVal e As NewAppointmentEventArgs)
Public Event AppointmentDblClicked(ByVal Appointment_Id As Integer)
Public Event AppointmentMoved(ByVal Appointment_Id As Integer, _
               ByVal OldDay As Integer, ByVal NewDay As Integer)

I used two custom EventArgs structures, MonthChangedEventArgs and NewAppointmentEventArgs. This was mainly because I'm using other information in the application I built; you could just pass a date, or an ID, or an actual appointment object.

To handle the events, just write an event handler as you would for any control. In the sample app's Window1.xaml.vb, I used the following:

Private Sub DayBoxDoubleClicked_event(ByVal e As NewAppointmentEventArgs) _
        Handles AptCalendar.DayBoxDoubleClicked
    MessageBox.Show("You double-clicked on day " & _
       CDate(e.StartDate).ToShortDateString(), _
       "Calendar Event", MessageBoxButton.OK)
End Sub

Private Sub AppointmentDblClicked(ByVal Appointment_Id As Integer) _
            Handles AptCalendar.AppointmentDblClicked
    MessageBox.Show("You double-clicked on appointment with ID = " & _
                    Appointment_Id, "Calendar Event", MessageBoxButton.OK)
End Sub

Private Sub AppointmentChanged_event(ByVal Appointment_Id As Integer, _
	ByVal OldDayOfMonth As Integer, ByVal NewDayOfMOnth As Integer) _
            Handles AptCalendar.AppointmentMoved
        MessageBox.Show("You moved appointment with ID = _
		" & Appointment_Id & " from day " & OldDayOfMonth & _
                	" to day " & NewDayOfMOnth, "Calendar Event", MessageBoxButton.OK)
End Sub

Private Sub DisplayMonthChanged(ByVal e As MonthChangedEventArgs) _
            Handles AptCalendar.DisplayMonthChanged
    Call SetAppointments()
End Sub

In my actual application (of which this is a tiny part), the Appointment class is a LINQ-to-SQL class, and has more fields and functionality. For this demo, I stripped out everything LINQ-specific.  MonthView stores appointments as a List(Of Appointment), which you set using the property MonthAppointments (ideally, you only pass the appointments the calendar needs to show that month). In this demo, I have a loop in the Window's Loaded event that creates appointments on 50 random days during the current year, and I pass only a filtered list (using List(Of T).FindAll) to MonthAppointments

Setting the MonthAppointments property will automatically redraw the calendar to show the currently selected month. There is one other public property, DisplayStartDate (of type Date), which is used to set the month and year to show (the day and time are ignored). Setting DisplayStartDate does not (possibly confusingly) cause the calendar to re-render with that month; this is because in my app, I always set some appointments (even if I assign an empty List(Of Appointment) to MonthAppointments). 

There is a label that shows the currently-displayed month and year, plus forward and back-buttons, which update DisplayStartDate, and then raise the DisplayMonthChanged event. Finally, the bit I added to this version - I made use of an attached property (defined in DragDropHelper) which attaches event-handlers to the events PreviewMouseLeftButtonDown, PreviewMouseLeftButtonUp, and PreviewMouseMove. The actual Appointment object is stored in the clipboard as a DataObject, with the format name set to GetType(Appointment).FullName, so that it should play nicely with whatever namespace you are using.

Points of Interest 

In order to tell where a user has double-clicked (since it could be on an appointment, or in the blank part of a daybox control), I repurposed a function that I'm using in another part of the app, which was originally in C# and comes from Bea Stollnitz's blog.

Also - I included a small bit of code in MonthView.xaml.vb that shows some random appointments in design mode (when System.ComponentModel.DesignerProperties.GetIsInDesignMode(Me) is true) - this was so I could see what the appointments looked like without having to build/run. Obviously, you can safely remove it.

Keep in mind that this month view was a quick & dirty solution to a problem, and it definitely has flaws. One main flaw is that if you have too many appointments in one day, they don't fit, and don't give you any visual cue. Also, the control is created with XAML building blocks, although you could grab the intermediate-built *.xaml.g.vb files and create a new project with all code, or just build it to a DLL and then reference it. As of now, it doesn't support styling, and this sample version doesn't support dragging to Outlook, or the desktop, etc. (my actual app does implement this, but it's probably not useful to many other people the way I do it). Hopefully this sample inspires someone who's better at WPF than I am to build something into the WPF toolkit Wink | ;-)

History

  • Version 0.2 - Posted August 21, 2009 (created earlier today, my time!) by Kirk Davis
    • This is an enhancement to the original project (which I linked to), which adds the ability to drag appointments around in the displayed month, and raises a new event when you do that.
  • Version 0.1 - Posted March 11, 2009 (created in Feb. 2009) by Kirk Davis 

License

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

Share

About the Author

kirkaiya
Software Developer (Senior)
Thailand Thailand
Just a .NET developer, mostly VB.NET and some C#, building applications and services using ASP.NET, WPF, WCF and Silverlight, as well as occasional project-management gigs.
 
Also - living abroad (in Thailand), originally planned for a year, but now just over seven years! Doh!

Comments and Discussions

 
QuestionScroll appointments [modified] Pinmembertraveus28-Jun-13 6:37 
QuestionMulti-day Events PinmemberPurviMehta8-Jan-13 8:44 
Questionbest Pinmemberpiseysen26-Dec-12 19:34 
QuestionC# code pls Pinmemberdanushka_l28-Sep-12 0:48 
Generalcan't run project PinmemberAsura02724-Jun-10 22:31 
error not support file type i use vs2008
GeneralDragnDrop PinmemberCodeProjectvb6-Apr-10 14:03 
GeneralGreat..! Pinmembervaduganathan25-Nov-09 18:37 
GeneralThis is great PinmemberMember 232448315-Oct-09 7:31 

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
Web02 | 2.8.140916.1 | Last Updated 21 Aug 2009
Article Copyright 2009 by kirkaiya
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid