Kreij
Senior Monkey Moderator
- Joined
- Feb 6, 2007
- Messages
- 13,817 (2.07/day)
- Location
- Cheeseland (Wisconsin, USA)
You have probably been thinking that old Uncle Kreij has been a bit quiet in the Programming
section lately. This should explain a bit about what I've been up to ....
I am writing a business management application for my wife's Massage Therapy business.
While working on the scheduling portion of the application, I wanted to use a
DateTimePicker (DTP) so that she could select dates to view in the scheduler. When you click
on the drop down part of the DTP, it displays a MonthCalendar control.
Unfortunately, the MonthCalendar control that is instantiated from the DTP does
not expose the methods and properties (like bolded dates) that the Normal MonthCalendar
control exposes.
So what is one to do? Write your own custom control
I figured that since I had to go through the trouble of writing a custom control, I might as well
add a bunch of funtionality to it.
My new MonthCalendar will allow the following:
..Bolded Dates
..Colored Dates
..Colored Date Backgrounds
..Colored Boxes surrounding the background.
..Any combination of the above.
Okay, first we need something to hold the date information that will be passed to the control
I chose to use an internal class (embedded in the UserControl). I could have used a struct type
but since I wanted it to have multiple constructors I felt that a class was more appropriate.
It is, however, completely valid to write a struct with multiple constructors.
Alrighty then, we have our class for date data. Now the fun begins.
We need to create a class that inherits the functionality of the MonthCalendar control
and override stuff so we can draw it the way we want. I named it MonCal. Pretty original, huh?
This class takes as input a typed List (List<T>) of the above class of date information.
The interesting part is that there is no easy way to determine exactly where we want to draw
on the control. The MonthCalendar control is divided into areas, but these areas cannot be accessed
through methods of anykind. Thankfully, we can determine where we are on the control by doing a HitTest
which when given a point on the control will return the area. There are three areas that are of interest to us.
The Date area, PrevMonthDate area, and NextMonthDate area. These are the areas which contain the actual
dates in the control. The rest of the areas are titles, button and other stuff.
The way we have to do the drawing is to override the WndProc method (which is a message pump) and
test for the WM_PAINT message. When we see it, trap on it and call our own custom OnPaint method.
Sounds like fun, right?
So let's do some code.
Hey! That wasn't so bad.
Now all we have to do is make a UserControl that contains and utilizes are classes.
Since the UserControl was generated in VS, there is some garbage collection and control iniialization
that is not included here.
Here is the ouput in the VS Control tester.
9-1-2008 is our highlighted date. 9-5-2008 is todays date and automatically highlighted by the base MonthCalendar functionaliy,
I have not gotten too detailed on how everything works, so if anyone has any questions or comments or code corrections, feel free to post.
I hope you enjoyed my little tutorial on modifying a MonthCalendar.
Next I will generate a custom DTP that will use this modified MonthCalendar as its drop down.
Have fun coding !!
section lately. This should explain a bit about what I've been up to ....
I am writing a business management application for my wife's Massage Therapy business.
While working on the scheduling portion of the application, I wanted to use a
DateTimePicker (DTP) so that she could select dates to view in the scheduler. When you click
on the drop down part of the DTP, it displays a MonthCalendar control.
Unfortunately, the MonthCalendar control that is instantiated from the DTP does
not expose the methods and properties (like bolded dates) that the Normal MonthCalendar
control exposes.
So what is one to do? Write your own custom control

I figured that since I had to go through the trouble of writing a custom control, I might as well
add a bunch of funtionality to it.
My new MonthCalendar will allow the following:
..Bolded Dates
..Colored Dates
..Colored Date Backgrounds
..Colored Boxes surrounding the background.
..Any combination of the above.
Okay, first we need something to hold the date information that will be passed to the control
I chose to use an internal class (embedded in the UserControl). I could have used a struct type
but since I wanted it to have multiple constructors I felt that a class was more appropriate.
It is, however, completely valid to write a struct with multiple constructors.
Code:
[color=blue]internal class[/color] [color=teal]HighlightedDates[/color]
{
[color=blue]public[/color] [color=teal]DateTime[/color] Date;
[color=blue]public[/color] [color=teal]Point[/color] Position = [color=blue]new[/color] [color=teal]Point[/color](0, 0);
[color=blue]public[/color] [color=teal]Color[/color] DateColor;
[color=blue]public[/color] [color=teal]Color[/color] BoxColor;
[color=blue]public[/color] [color=teal]Color[/color] BackgroundColor;
[color=blue]public[/color] [color=teal]Boolean[/color] Bold;
[color=green]// This constructor is used if you only want to make dates bold. All colors are set to "Empty"(null color)[/color]
[color=blue]public[/color] HighlighedDates([color=teal]DateTime[/color] date);
{
[color=blue]this[/color].Date = date;
[color=blue]this[/color].DateColor = [color=blue]this[/color].BoxColor = [color=blue]this[/color].BackgroundColor = [color=teal]Color[/color].Empty;
[color=blue]this[/color].Bold = [color=blue]true[/color];
}
[color=green]// This constructor is used if you want colored and/or bolded dates[/color]
[color=blue]public[/color] HighlighedDates([color=teal]DateTime[/color] date, [color=teal]Color[/color] dateColor, [color=teal]Boolean[/color] bold);
{
[color=blue]this[/color].Date = date;
[color=blue]this[/color].DateColor = dateColor;
[color=blue]this[/color].BoxColor = [color=blue]this[/color].BackgroundColor = [color=teal]Color[/color].Empty;
[color=blue]this[/color].Bold = bold;
}
[color=green]// This constructor is used when you want to control everything[/color]
[color=blue]public[/color] HighlightedDates([color=teal]DateTime[/color] date, [color=teal]Color[/color] dateColor, [color=teal]Color[/color] boxColor,
[color=teal]Color[/color] backgroundColor, [color=teal]Boolean[/color] bold)
{
[color=blue]this[/color].Date = date;
[color=blue]this[/color].DateColor = dateColor;
[color=blue]this[/color].BoxColor = boxColor;
[color=blue]this[/color].BackgroundColor = backgroundColor;
[color=blue]this[/color].Bold = bold;
}
}
Alrighty then, we have our class for date data. Now the fun begins.
We need to create a class that inherits the functionality of the MonthCalendar control
and override stuff so we can draw it the way we want. I named it MonCal. Pretty original, huh?
This class takes as input a typed List (List<T>) of the above class of date information.
The interesting part is that there is no easy way to determine exactly where we want to draw
on the control. The MonthCalendar control is divided into areas, but these areas cannot be accessed
through methods of anykind. Thankfully, we can determine where we are on the control by doing a HitTest
which when given a point on the control will return the area. There are three areas that are of interest to us.
The Date area, PrevMonthDate area, and NextMonthDate area. These are the areas which contain the actual
dates in the control. The rest of the areas are titles, button and other stuff.
The way we have to do the drawing is to override the WndProc method (which is a message pump) and
test for the WM_PAINT message. When we see it, trap on it and call our own custom OnPaint method.
Sounds like fun, right?
So let's do some code.
Code:
[color=blue]internal class[/color] [color=teal]MonCal[/color] : [color=teal]MonthCalendar[/color]
{
[color=blue]protected static int[/color] WM_PAINT = 0x000F;
[color=blue]private[/color] [color=teal]Rectangle[/color] dayBox;
[color=blue]private int[/color] dayTop = 0;
[color=blue]private[/color] [color=teal]SelectionRange[/color] range;
[color=blue]private[/color] [color=teal]List[/color]<[color=teal]HighlightedDates[/color]> highlightedDates = [color=blue]new[/color] [color=teal]List[/color]<[color=teal]HighlightedDates[/color]>();
[color=blue]public[/color] MonCal([color=teal]List[/color]<[color=teal]HighlightedDates[/color]> HighlightedDates)
{
[color=blue]this[/color].ShowTodayCircle = [color=blue]false[/color];
[color=blue]this[/color].highlightedDates = HighlightedDates;
range = GetDisplayRange([color=blue]false[/color]);
SetDayBoxSize();
SetPosition([color=blue]this[/color].highlightedDates);
}
[color=green]// This method figures out the size of the entire date area portion of the control
// and then divides it up o create a Rectagle for painting to individual dates[/color]
[color=blue]private void[/color] SetDayBoxSize()
{
[color=blue]int[/color] bottom = [color=blue]this[/color].Height;
[color=blue]while[/color] (HitTest(1, dayTop).HitArea != [color=eal]HitArea[/color].Date &&
HitTest(1, dayTop).HitArea != [color=teal]HitArea[/color].PrevMonthDate) dayTop++;
[color=blue]while[/color] (HitTest(1, bottom).HitArea != [color=teal]HitArea[/color].Date &&
HitTest(1, bottom).HitArea != [color=teal]HitArea[/color].NextMonthDate) bottom--;
dayBox = [color=blue]new[/color] [color=teal]Rectangle[/color]();
dayBox.Size = [color=blue]new[/color] [color=teal]Size[/color]([color=blue]this[/color].Width / 7, (bottom - dayTop) / 6);
}
[color=green]// This method determines where in the 7 x 6 array of dates on the control our highlighted dates reside.[/color]
[color=blue]private void[/color] SetPosition([color=teal]List[/color]<[color=teal]HighlightedDates[/color]> hlDates)
{
[color=blue]int[/color] row = 0, col = 0;
hlDates.ForEach([color=blue]delegate[/color]([color=teal]HighlightedDates[/color] date)
{
[color=blue]if[/color] (date.Date >= range.Start && date.Date <= range.End)
{
[color=teal]TimeSpan[/color] span = date.Date.Subtract(range.Start);
row = span.Days / 7;
col = span.Days % 7;
date.Position = [color=blue]new[/color] [color=teal]Point[/color](row, col);
}
});
}
[color=green]// This overrides the message pump and traps the WM_PAINT call[/color]
[color=blue]protected override void[/color] WndProc([color=blue]ref[/color] [color=teal]Message[/color] m)
{
[color=blue]base[/color].WndProc([color=blue]ref[/color] m);
[color=blue]if[/color] (m.Msg == WM_PAINT)
{
[color=teal]Graphics[/color] g = [color=teal]Graphics[/color].FromHwnd([color=blue]this[/color].Handle);
[color=teal]PaintEventArgs[/color] pea =
[color=blue]new[/color] [color=teal]PaintEventArgs[/color](g, [color=blue]new[/color] [color=teal]Rectangle[/color](0, 0, [color=blue]this[/color].Width, [color=blue]this[/color].Height));
OnPaint(pea);
}
}
[color=green]// Here is where we use our information to selectively draw what we want[/color]
[color=blue]protected override void[/color] OnPaint([color=teal]PaintEventArgs[/color] e)
{
[color=blue]base[/color].OnPaint(e);
[color=eal]Graphics[/color] g = e.Graphics;
[color=teal]Rectangle[/color] backgroundRect;
highlightedDates.ForEach([color=blue]delegate[/color]([color=teal]HighlightedDates[/color] date)
{
backgroundRect = [color=blue]new[/color] [color=teal]Rectangle[/color](
date.Position.Y * dayBox.Width + 1,
date.Position.X * dayBox.Height + dayTop,
dayBox.Width, dayBox.Height);
[color=blue]if[/color] (date.BackgroundColor != [color=teal]Color[/color].Empty)
{
[color=blue]using[/color] ([color=teal]Brush[/color] brush = [color=blue]new[/color] [color=teal]SolidBrush[/color](date.BackgroundColor))
{
g.FillRectangle(brush, backgroundRect);
}
[color=blue]if[/color] (date.Bold || date.DateColor != [color=teal]Color[/color].Empty)
{
[color=blue]using[/color] ([color=teal]Font[/color] textFont =
[color=blue]new[/color] [color=teal]Font[/color](Font, (date.Bold ? [color=teal]FontStyle[/color].Bold : [color=teal]FontStyle[/color].Regular)))
{
[color=teal]TextRenderer[/color].DrawText(g, date.Date.Day.ToString(), textFont,
backgroundRect, date.DateColor,
[color=teal]TextFormatFlags[/color].HorizontalCenter | [color=teal]TextFormatFlags[/color].VerticalCenter);
}
}
[color=blue]if[/color] (date.BoxColor != [color=teal]Color[/color].Empty)
{
[color=blue]using[/color] ([color=teal]Pen[/color] pen = [color=blue]new[/color] [color=teal]Pen[/color](date.BoxColor))
{
[color=teal]Rectangle[/color] boxRect = [color=blue]new[/color] [color=teal]Rectangle[/color](
date.Position.Y * dayBox.Width + 1,
date.Position.X * dayBox.Height + dayTop,
dayBox.Width, dayBox.Height);
g.DrawRectangle(pen, boxRect);
}
}
});
}
}
Hey! That wasn't so bad.
Now all we have to do is make a UserControl that contains and utilizes are classes.
Code:
[color=blue]public partial class[/color] [color=teal]NFXMonthCalendar[/color] : [color=teal]UserControl[/color]
{
[color=blue]private[/color] [color=teal]List[/color]<[color=teal]HighlightedDates[/color]> highLightedDates;
[color=blue]public[/color] NFXMonthCalendar()
{
InitializeComponent();
[color=green]// Dates would normally be passed in, in a List. For testing purposes I added the next declaration[/color]
highLightedDates.Add([color=blue]new[/color] [color=teal]HighlightedDates[/color]([color=teal]Convert[/color].ToDateTime([color=red]"9/1/2008"[/color]),
[color=teal]Color[/color].Red, [color=teal]Color[/color].Blue, [color=teal]Color[/color].Pink, [color=blue]true[/color]));
[color=teal]MonCal[/color] mCal = [color=blue]new[/color] [color=teal]MonCal[/color](highLightedDates);
[color=blue]this[/color].Controls.Add(mCal);
}
}
[color=green]// Add MonCal class here[/color]
[color=green]// Add HighlightedDates class here[/color]
}
Since the UserControl was generated in VS, there is some garbage collection and control iniialization
that is not included here.
Here is the ouput in the VS Control tester.
9-1-2008 is our highlighted date. 9-5-2008 is todays date and automatically highlighted by the base MonthCalendar functionaliy,

I have not gotten too detailed on how everything works, so if anyone has any questions or comments or code corrections, feel free to post.
I hope you enjoyed my little tutorial on modifying a MonthCalendar.
Next I will generate a custom DTP that will use this modified MonthCalendar as its drop down.
Have fun coding !!
Last edited: