Introduction
This article describes a month calendar control in iCal style with fixed and custom selection ranges, holidays highlight and customizable appearance. Here is a screenshot:
Background
Writing my diploma project, I needed a calendar control that can work with fixed selection ranges (day, week, month) or custom ranges. Besides it must support highlighted holidays and customizable appearance. I found some commercial and free toolkits, but none of them met my requirements and majority of them look rather ugly. What I wanted was something like iCal navigation calendar control. So I tried to draw it myself from nothing.
Using the Code
This sample shows how to instantiate a control and use it.
this.ww_monthcal1.BackCalDates = System.Drawing.Color.White;
this.ww_monthcal1.BackSelDates = System.Drawing.Color.Gray;
this.ww_monthcal1.Dock = System.Windows.Forms.DockStyle.Fill;
this.ww_monthcal1.Holid = System.Drawing.Color.Red;
this.ww_monthcal1.Location = new System.Drawing.Point(0, 0);
this.ww_monthcal1.MinimumSize = new System.Drawing.Size(124, 106);
this.ww_monthcal1.Name = "ww_monthcal1";
this.ww_monthcal1.SelectionMode = WorkWeek.ww_monthcal.SMode.Day;
this.ww_monthcal1.Size = new System.Drawing.Size(257, 337);
this.ww_monthcal1.Start = new System.DateTime(2010, 3, 22, 0, 0, 0, 0);
this.ww_monthcal1.TabIndex = 0;
this.ww_monthcal1.Text = "ww_monthcal1";
this.ww_monthcal1.SELCHANGE += new WorkWeek.ww_monthcal.SELECTION_CHANGE
(this.ww_monthcal1_SELCHANGE);
private void ww_monthcal1_SELCHANGE(DateTime Start, DateTime End)
{
Text=Start.ToString()+" - "+End.ToString();
}
Solution
Here are some interesting functions that realize inner control logic and can be helpful.
private string localdaynameFROMindex(int index)
{
CultureInfo local = CultureInfo.CurrentCulture;
int daynn = (int)(System.Globalization.CultureInfo.
CurrentCulture.DateTimeFormat.FirstDayOfWeek); int actualdayn = daynn + index;
if (actualdayn == 7) actualdayn = 0;
return local.DateTimeFormat.GetAbbreviatedDayName((DayOfWeek)actualdayn);
}
private Rectangle DATERECT(DateTime dt)
{
DateTime monthst = new DateTime(dt.Year, dt.Month, 1);
int x = 0;
int y = 0;
int dy = this.Font.Height;
int dx = this.Width / 7;
int daystostartday = 0;
int daynv = (int)monthst.DayOfWeek; int daynn = (int)(System.Globalization.CultureInfo.
CurrentCulture.DateTimeFormat.FirstDayOfWeek); if (daynn <= daynv)
daystostartday = daynv - daynn;
else
daystostartday = 7 - daynn - daynv;
x = (int)((int)(((dt - monthst).TotalDays + daystostartday) % 7) * dx) + 1;
y = (int)((int)(((dt - monthst).TotalDays + daystostartday) / 7) * dy) + 2 * dy;
return new Rectangle(x, y, dx, dy);
}
private DateTime XYtoDATE(int X, int Y, int list)
{
DateTime dt = new DateTime(mStart.Year, mStart.Month, 1).AddMonths(list);
int daystostartday = 0;
int daynv = (int)dt.DayOfWeek; int daynn = (int)(System.Globalization.CultureInfo.CurrentCulture.
DateTimeFormat.FirstDayOfWeek); if (daynn <= daynv)
daystostartday = daynv - daynn;
else
daystostartday = 7 - daynn - daynv;
int dy = this.Font.Height;
int dx = this.Width / 7;
int XYcolumn = (int)(X / dx);
int XYstring = (int)((Y - 2 * dy) / dy);
int adddays = 7 * (XYstring) + XYcolumn - daystostartday;
if (adddays < 0)
adddays = 0;
if (adddays >= DateTime.DaysInMonth(dt.Year, dt.Month))
adddays = DateTime.DaysInMonth(dt.Year, dt.Month) - 1;
dt = dt.AddDays(adddays);
return dt;
}
Points of Interest
Lots of calendar controls I found just extend the functionality of standard month calendar control that brings lots of problems with design and appearance of control. Drawing a control actually wasn’t as hard as it seemed before.
Some problems come from the fact that week can start with different days in different countries, but doing some manipulations with System.Globalization.CultureInfo
solved all problems.
History
- First version published on 6/04/2010