Click here to Skip to main content
15,922,584 members
Articles / Desktop Programming / MFC

Calendar DayView Control

Rate me:
Please Sign up or sign in to vote.
4.86/5 (152 votes)
20 Jun 2006CPOL3 min read 1.7M   24.9K   375   510
A calendar DayView control.

Introduction

This article describes creating a day view control for visualizing schedule functions in required applications. I have cloned the Outlook day view appearance for similar use. Here is a screenshot:

Office 12 Theme:

Sample Image

Office XP Theme:

Sample Image

Background

Before writing this control, I was in need of a day view control that just looks like that in Outlook. I have found some commercial toolkits but none of them meets my requirements. Some of those want that all appointments be given before showing the control, some of those are not open source, etc. So I wrote this control in a "hurry development", and I think other people can use it. Pay back time for using the CodeProject :)

What we have?

  • You can create your appointment class to hold special information (other than start, end dates and title).
  • You don't need to read all appointments from the DB or something like that.
  • You can specify how much days will be shown.
  • You can colorize appointments to show different views.
  • In-place editing.
  • Drag drop operations.
  • No Win32 API.
  • Theme based rendering.

By the way, it's compiled under the final release of .NET 2.0. If you don't have it, you need to recompile the project.

Using the code

This control uses a class named "Appointment" to visualize the view. The DayView control doesn't pay attention to saving appointments to the DB or fetching them. So, you need to write your own DB logic and answer the events.

The control implements these events to interact with the hosting application:

  • dayView1.NewAppointment
  • dayView1.ResolveAppointments
  • dayView1.SelectionChanged

The sample application uses a list collection as the container for appointments. You may use a cached DB source too.

NewAppointment event

This is raised when the user wants to create an appointment. Event arguments contain start date, end date, and title values of the new appointment. You can create your appointment class that inherits from the DayView.Appointment base class.

The sample application just creates a new appointment, and adds it to the list collection.

C#
void dayView1_NewAppointment(object sender, NewAppointmentEventArgs args)
{
    Appointment m_Appointment = new Appointment();

    m_Appointment.StartDate = args.StartDate;
    m_Appointment.EndDate = args.EndDate;
    m_Appointment.Title = args.Title;

    m_Appointments.Add(m_Appointment);
}

ResolveAppointments event

This event is raised when the DayView control needs to show an appointment on a date. Event arguments contain the start date and end date of the required range of dates.

The sample application scans the list collection for a specified date range. You can fetch them from your own DB too.

C#
private void dayView1_ResolveAppointments(object sender, 
                      ResolveAppointmentsEventArgs args)
{
    List<Appointment> m_Apps = new List<Appointment>();

    foreach (Appointment m_App in m_Appointments)
        if ((m_App.StartDate >= args.StartDate) && 
            (m_App.StartDate <= args.EndDate))
            m_Apps.Add(m_App);

    args.Appointments = m_Apps;
}

Selection Changed event

This event is raised when the user selects an appointment.

C#
private void dayView1_SelectionChanged(object sender, EventArgs e)
{
    label3.Text = dayView1.SelectionStart.ToString() + 
                  ":" + dayView1.SelectionEnd.ToString();
}

The sample application shows the start date and the end date of the selected appointment in a label.

Points of interest

When I wrote this control, the hard part was sorting the appointments on screen without crossing over. The control internally uses an "AppointmentView" class to hold the state of appointments on screen. The rest of the code was drawing and isolating from the external application.

You may see some remarked codes about all-day events, but currently all-day events is not complete. When I finish it, I'll update this article. (Thanks to Claus Espersen for implementing the Office 12 theme and bug fixes.)

History

  • 14.07.2006
    • Bug fixes.
    • Start hour and start minute properties implemented.
    • Theme based rendering implemented.
    • AllowNew property implemented.
    • Mouse drag drop bugs fixed.
    • Internal changes for all day events.
    • Zoom feature implemented.
  • 11.09.2005
    • Initial release.

License

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


Written By
Web Developer
Turkey Turkey
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionIn web applicaiton? Pin
isroavi8-Jul-07 16:28
isroavi8-Jul-07 16:28 
QuestionHow to use Grouping? Pin
DaShiva7-Jul-07 18:50
DaShiva7-Jul-07 18:50 
GeneralASP.NET Pin
SammyDog229-Jun-07 5:10
SammyDog229-Jun-07 5:10 
Generalmulti persons. Pin
TrevorWJ26-Jun-07 1:37
TrevorWJ26-Jun-07 1:37 
GeneralSmall bug in GetMaxParalelAppointments [modified] Pin
flow196525-Jun-07 4:16
flow196525-Jun-07 4:16 
GeneralRe: Small bug in GetMaxParalelAppointments Pin
elenarig27-Jun-07 3:44
elenarig27-Jun-07 3:44 
GeneralRe: Small bug in GetMaxParalelAppointments Pin
flow196527-Jun-07 22:27
flow196527-Jun-07 22:27 
GeneralRe: Small bug in GetMaxParalelAppointments Pin
elenarig28-Jun-07 1:40
elenarig28-Jun-07 1:40 
Resolved modifing the routine in object and the DrawAppointments one

I post it here but it has comments in italian and is in vb.net

I assume the maximum width for each app of the day equal to the day width divided by the max number of collisions.

Seems to work but in some cases could be not so pretty....

****************************************************************************************
Private Sub DrawAppointments(ByVal e As PaintEventArgs, ByVal rect As Rectangle, ByVal time As DateTime)

'ora di inizio del giorno da disegnare
Dim timeStart As DateTime = time.Date

'ora di fine del giorno da disegnare - 1 secondo per rimanere nello stesso dì
Dim timeEnd As DateTime = timeStart.AddHours(24)
timeEnd = timeEnd.AddSeconds(-1)

'lista degli appuntamenti per il giorno
Dim appointments As AppointmentList = DirectCast(cachedAppointments(time.Day), AppointmentList)

'se ne esiste almeno uno...
If appointments IsNot Nothing Then

'determina le sovrapposizioni nelle mezzore
Dim layout As HalfHourLayout() = GetMaxParallelAppointments(appointments)
Dim drawnItems As New List(Of Appointment)

'**********************************************************************
'my method

'determina il numero di conflitti massimo per la giornata
Dim maxConflicts As Integer = 1
For halfHour As Integer = 0 To 24 * 2 - 1

Dim hourLayout As HalfHourLayout = layout(halfHour)

If (hourLayout IsNot Nothing) AndAlso (hourLayout.Count > 0) Then

If hourLayout.Count > maxConflicts Then
maxConflicts = hourLayout.Count
End If
End If
Next

'determina la larghezza di ciascun appuntamento
Dim appointmentsWidth As Integer
appointmentsWidth = rect.Width / maxConflicts

'definisce una matrice di occupazione del giorno
'X: numero di suddivisioni (unità); Y: numero di mezzore nel giorno
Dim occupation(maxConflicts - 1, 24 * 2 - 1) As Boolean

'dispone l'elemento
For Each myApp As Appointment In appointments

'dimensiona il rettangolo dell'appuntamento, inizialmente uguale all'intera giornata
Dim appRect As Rectangle = rect

'assegna la larghezza prefissata all'appuntamento (leggermente ridotta)
appRect.Width = appointmentsWidth - 5

'restituisce il rettangolo dimensionato sull'altezza che deve occupare
'e posizionato in Y in base all'ora di inizio
appRect = GetHourRangeRectangle(myApp.StartDate, myApp.EndDate, appRect)

'tacca della mezzora di inizio
Dim firstHH As Integer = myApp.StartDate.Hour * 2 + (myApp.StartDate.Minute / 30)

'tacca della mezzora di fine
Dim lastHH As Integer = myApp.EndDate.Hour * 2 + (myApp.EndDate.Minute / 30)

'individua la X di posizionamento
Dim myX As Integer = 0

'scorro le colonne in X
For i As Integer = 0 To maxConflicts - 1

'se trovo posizione libera
If Not occupation(i, firstHH) Then

'inizializzo variabile di collisione a False
Dim collision As Boolean = False

'controllo che sia libero lo spazio sottostante
For h As Integer = firstHH To lastHH - 1
If occupation(i, h) Then
collision = True
Exit For
End If
Next

'se lo spazio è disponibile
If Not collision Then

'memorizzo la posizione per la X
myX = i

'memorizza sulle fasce occupate la presenza dell'appuntamento
For h As Integer = firstHH To lastHH - 1
occupation(myX, h) = True
Next
Exit For
End If

End If
Next

'assegna la X al rettangolo
appRect.X = rect.X + appointmentsWidth * myX

Dim view As AppointmentView = New AppointmentView()
view.Rectangle = appRect
view.Appointment = myApp

appointmentViews(myApp) = view

'traccia graficamente il rettangolo
e.Graphics.SetClip(rect)
m_renderer.DrawAppointment(e.Graphics, appRect, myApp, myApp Is m_selectedAppointment, appointmentGripWidth)
e.Graphics.ResetClip()


Next
'original method

''**********************************************************************
''per ciascun intervallo (mezzora)
' For halfHour As Integer = 0 To 24 * 2 - 1

' Dim hourLayout As HalfHourLayout = layout(halfHour)

' If (hourLayout IsNot Nothing) AndAlso (hourLayout.Count > 0) Then
' For appIndex As Integer = 0 To hourLayout.Count - 1
' Dim appointment As Appointment = hourLayout.Appointments(appIndex)

' If drawnItems.IndexOf(appointment) < 0 Then
' Dim appRect As Rectangle = rect
' Dim appointmentWidth As Integer
' Dim view As AppointmentView

' appointmentWidth = rect.Width / appointment.m_ConflictCount

' Dim lastX As Integer = 0

' For Each app As Appointment In hourLayout.Appointments
' If (app IsNot Nothing) AndAlso (appointmentViews.ContainsKey(app)) Then
' view = appointmentViews(app)

' If lastX < view.Rectangle.X Then
' lastX = view.Rectangle.X
' End If
' End If
' Next

' 'è da commentare?!?!?!
' If (lastX + (appointmentWidth * 2)) > (rect.X + rect.Width) Then
' lastX = 0
' End If
' '

' appRect.Width = appointmentWidth - 5

' If lastX > 0 Then
' appRect.X = lastX + appointmentWidth
' End If

' appRect = GetHourRangeRectangle(appointment.StartDate, appointment.EndDate, appRect)

' view = New AppointmentView()
' view.Rectangle = appRect
' view.Appointment = appointment

' appointmentViews(appointment) = view

' 'MsgBox(appointment.Cliente)
' 'MsgBox(appRect.X.ToString & " " & appRect.Y.ToString)
' 'MsgBox(appRect.Width.ToString & " " & appRect.Height.ToString)


' e.Graphics.SetClip(rect)

' m_renderer.DrawAppointment(e.Graphics, appRect, appointment, appointment Is m_selectedAppointment, appointmentGripWidth)

' e.Graphics.ResetClip()

' drawnItems.Add(appointment)
' End If
' Next
' End If
' Next
End If
End Sub
GeneralRe: Small bug in GetMaxParalelAppointments Pin
DaShiva7-Jul-07 21:42
DaShiva7-Jul-07 21:42 
GeneralRe: Small bug in GetMaxParalelAppointments Pin
elenarig9-Jul-07 20:54
elenarig9-Jul-07 20:54 
GeneralRe: Small bug in GetMaxParalelAppointments Pin
Claus7T9-Sep-07 23:02
Claus7T9-Sep-07 23:02 
GeneralRe: Small bug in GetMaxParalelAppointments Pin
Claus7T10-Sep-07 2:37
Claus7T10-Sep-07 2:37 
QuestionHow do I register the DLL Pin
streetcar16-Jun-07 16:14
streetcar16-Jun-07 16:14 
AnswerRe: How do I register the DLL Pin
peppe antonini15-Oct-07 6:16
peppe antonini15-Oct-07 6:16 
GeneralRe: How do I register the DLL Pin
streetcar15-Oct-07 13:35
streetcar15-Oct-07 13:35 
GeneralRe: How do I register the DLL Pin
Snerf20-Nov-07 12:52
Snerf20-Nov-07 12:52 
GeneralPlease Help!!! Printing DayView Pin
niemkhuccuoi07018-Jun-07 11:04
niemkhuccuoi07018-Jun-07 11:04 
GeneralRe: Please Help!!! Printing DayView Pin
DaDoo7011-Jun-07 21:05
DaDoo7011-Jun-07 21:05 
GeneralRe: Please Help!!! Printing DayView Pin
Claus7T13-Jun-07 22:30
Claus7T13-Jun-07 22:30 
QuestionExtracting/Importing Pin
ClaudeX6-Jun-07 23:02
ClaudeX6-Jun-07 23:02 
GeneralDefault interval 30 min Pin
jfc94164024-May-07 6:49
jfc94164024-May-07 6:49 
GeneralRe: Default interval 30 min Pin
Ertan Tike28-May-07 10:13
Ertan Tike28-May-07 10:13 
GeneralRe: Default interval 30 min , Pin
hermann00714-Jun-07 21:37
hermann00714-Jun-07 21:37 
GeneralRe: Default interval 30 min , Pin
Claus7T21-Jun-07 0:02
Claus7T21-Jun-07 0:02 
GeneralRe: Default interval 30 min , Pin
hermann00722-Jun-07 2:39
hermann00722-Jun-07 2:39 

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.