带有EnableVisualStyles的MonthCalendar控件选择范围?

我正在使用MonthCalendar控件,并希望以编程方式选择日期范围。 当我这样做时,如果调用Application.EnableVisualStyles() ,控件将无法正确绘制。 根据MSDN,这是一个已知问题。

使用启用了视觉样式的MonthCalendar将导致MonthCalendar控件的选择范围无法正确绘制(来自: http : //msdn.microsoft.com/en-us/library/system.windows.forms.monthcalendar.aspx )

除了不调用EnableVisualStyles之外,还有其他方法吗? 这似乎使得这种特殊控制对于一系列应用来说完全无用,而且从我的角度来看这是一个相当明显的疏忽。

在寻找同一问题的解决方案时,我首先在这里遇到了这个问题,但后来我发现了Nicke Andersson的一篇博客文章。 我觉得非常有帮助。 这是我对Nicke的例子所做的:

 public class MonthCalendarEx : System.Windows.Forms.MonthCalendar { private int _offsetX; private int _offsetY; private int _dayBoxWidth; private int _dayBoxHeight; private bool _repaintSelectedDays = false; public MonthCalendarEx() : base() { OnSizeChanged(null, null); this.SizeChanged += OnSizeChanged; this.DateChanged += OnSelectionChanged; this.DateSelected += OnSelectionChanged; } protected static int WM_PAINT = 0x000F; protected override void WndProc(ref System.Windows.Forms.Message m) { base.WndProc(ref m); if (m.Msg == WM_PAINT) { Graphics graphics = Graphics.FromHwnd(this.Handle); PaintEventArgs pe = new PaintEventArgs( graphics, new Rectangle(0, 0, this.Width, this.Height)); OnPaint(pe); } } private void OnSelectionChanged(object sender, EventArgs e) { _repaintSelectedDays = true; } private void OnSizeChanged(object sender, EventArgs e) { _offsetX = 0; _offsetY = 0; // determine Y offset of days area while ( HitTest(Width / 2, _offsetY).HitArea != HitArea.PrevMonthDate && HitTest(Width / 2, _offsetY).HitArea != HitArea.Date) { _offsetY++; } // determine X offset of days area while (HitTest(_offsetX, Height / 2).HitArea != HitArea.Date) { _offsetX++; } // determine width of a single day box _dayBoxWidth = 0; DateTime dt1 = HitTest(Width / 2, _offsetY).Time; while (HitTest(Width / 2, _offsetY + _dayBoxHeight).Time == dt1) { _dayBoxHeight++; } // determine height of a single day box _dayBoxWidth = 0; DateTime dt2 = HitTest(_offsetX, Height / 2).Time; while (HitTest(_offsetX + _dayBoxWidth, Height / 2).Time == dt2) { _dayBoxWidth++; } } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); if (_repaintSelectedDays) { Graphics graphics = e.Graphics; SelectionRange calendarRange = GetDisplayRange(false); Rectangle currentDayFrame = new Rectangle( -1, -1, _dayBoxWidth, _dayBoxHeight); DateTime current = SelectionStart; while (current <= SelectionEnd) { Rectangle currentDayRectangle; using (Brush selectionBrush = new SolidBrush( Color.FromArgb( 255, System.Drawing.SystemColors.ActiveCaption))) { TimeSpan span = current.Subtract(calendarRange.Start); int row = span.Days / 7; int col = span.Days % 7; currentDayRectangle = new Rectangle( _offsetX + (col + (ShowWeekNumbers ? 1 : 0)) * _dayBoxWidth, _offsetY + row * _dayBoxHeight, _dayBoxWidth, _dayBoxHeight); graphics.FillRectangle(selectionBrush, currentDayRectangle); } TextRenderer.DrawText( graphics, current.Day.ToString(), Font, currentDayRectangle, System.Drawing.SystemColors.ActiveCaptionText, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter); if (current == this.TodayDate) { currentDayFrame = currentDayRectangle; } current = current.AddDays(1); } if (currentDayFrame.X > 0) { graphics.DrawRectangle(new Pen( new SolidBrush(Color.Red)), currentDayFrame); } _repaintSelectedDays = false; } } } 

我在上面的Mark Cranness代码中发现了一个小问题:在完全禁用视觉样式的XP系统上,即使调用Application.EnableVisualStyles(),Application.RenderWithVisualStyles也会设置为False。

因此,在这种情况下,自定义绘制代码根本不会运行。 为了解决这个问题,我将FixVisualStylesMonthCalendar构造函数的第一行更改为

 if (Application.VisualStyleState != System.Windows.Forms.VisualStyles.VisualStyleState.NoneEnabled && Environment.OSVersion.Version < new Version(6, 0)) 

整个代码位于此答案的底部。

我找不到任何评论答案本身的方法。 下面代码的积分转到原作者 - (如果他或任何人都可以validation这个答案并更新它,我很乐意将其删除)

 ///  /// When Visual Styles are enabled on Windows XP, the MonthCalendar.SelectionRange /// does not paint correctly when more than one date is selected. /// See: http://msdn.microsoft.com/en-us/library/5d1acks5(VS.80).aspx /// "Additionally, if you enable visual styles on some controls, the control might display incorrectly /// in certain situations. These include the MonthCalendar control with a selection range set... /// This class fixes that problem. ///  /// Author: Mark Cranness - PatronBase Limited. public class FixVisualStylesMonthCalendar : System.Windows.Forms.MonthCalendar { ///  /// The width of a single cell (date) in the calendar. ///  private int dayCellWidth; ///  /// The height of a single cell (date) in the calendar. ///  private int dayCellHeight; ///  /// The calendar first day of the week actually used. ///  private DayOfWeek calendarFirstDayOfWeek; ///  /// Only repaint when VisualStyles enabled on Windows XP. ///  private bool repaintSelectionRange = false; ///  /// A MonthCalendar class that fixes SelectionRange painting problems /// on Windows XP when Visual Styles is enabled. ///  public FixVisualStylesMonthCalendar() { if (Application.VisualStyleState != System.Windows.Forms.VisualStyles.VisualStyleState.NoneEnabled && //Application.RenderWithVisualStyles && Environment.OSVersion.Version < new Version(6, 0)) { // If Visual Styles are enabled, and XP, then fix-up the painting of SelectionRange this.repaintSelectionRange = true; this.OnSizeChanged(this, EventArgs.Empty); this.SizeChanged += new EventHandler(this.OnSizeChanged); } } ///  /// The WM_PAINT message is sent to make a request to paint a portion of a window. ///  public const int WM_PAINT = 0x000F; ///  /// Override WM_PAINT to repaint the selection range. ///  [System.Diagnostics.DebuggerStepThroughAttribute()] protected override void WndProc(ref Message m) { base.WndProc(ref m); if (m.Msg == WM_PAINT && !this.DesignMode && this.repaintSelectionRange) { // MonthCalendar is ControlStyles.UserPaint=false => Paint event is not raised this.RepaintSelectionRange(ref m); } } ///  /// Repaint the SelectionRange. ///  private void RepaintSelectionRange(ref Message m) { using (Graphics graphics = this.CreateGraphics()) using (Brush backBrush = new SolidBrush(graphics.GetNearestColor(this.BackColor))) using (Brush selectionBrush = new SolidBrush(graphics.GetNearestColor(SystemColors.ActiveCaption))) { Rectangle todayFrame = Rectangle.Empty; // For each day in SelectionRange... for (DateTime selectionDate = this.SelectionStart; selectionDate <= this.SelectionEnd; selectionDate = selectionDate.AddDays(1)) { Rectangle selectionDayRectangle = this.GetSelectionDayRectangle(selectionDate); if (selectionDayRectangle.IsEmpty) continue; if (selectionDate.Date == this.TodayDate) { todayFrame = selectionDayRectangle; } // Paint as 'selected' a little smaller than the whole rectangle Rectangle highlightRectangle = Rectangle.Inflate(selectionDayRectangle, 0, -2); if (selectionDate == this.SelectionStart) { highlightRectangle.X += 2; highlightRectangle.Width -= 2; } if (selectionDate == this.SelectionEnd) { highlightRectangle.Width -= 2; } // Paint background, selection and day-of-month text graphics.FillRectangle(backBrush, selectionDayRectangle); graphics.FillRectangle(selectionBrush, highlightRectangle); TextRenderer.DrawText( graphics, selectionDate.Day.ToString(), this.Font, selectionDayRectangle, SystemColors.ActiveCaptionText, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter); } if (this.ShowTodayCircle && !todayFrame.IsEmpty) { // Redraw the ShowTodayCircle (square) that we painted over above using (Pen redPen = new Pen(Color.Red)) { todayFrame.Width--; todayFrame.Height--; graphics.DrawRectangle(redPen, todayFrame); } } } } ///  /// When displayed dates changed, clear the cached month locations. ///  private SelectionRange previousDisplayedDates = new SelectionRange(); ///  /// Gets a graphics Rectangle for the area corresponding to a single date on the calendar. ///  private Rectangle GetSelectionDayRectangle(DateTime selectionDateTime) { // Handle the leading and trailing dates from the previous and next months SelectionRange allDisplayedDates = this.GetDisplayRange(false); SelectionRange fullMonthDates = this.GetDisplayRange(true); int adjust1Week; DateTime selectionDate = selectionDateTime.Date; if (selectionDate < allDisplayedDates.Start || selectionDate > allDisplayedDates.End) { // Selection Date is not displayed on calendar return Rectangle.Empty; } else if (selectionDate < fullMonthDates.Start) { // Selection Date is trailing from the previous partial month selectionDate = selectionDate.AddDays(7); adjust1Week = -1; } else if (selectionDate > fullMonthDates.End) { // Selection Date is leading from the next partial month selectionDate = selectionDate.AddDays(-14); adjust1Week = +2; } else { // A mainline date adjust1Week = 0; } // Discard cached month locations when calendar moves if (this.previousDisplayedDates.Start != allDisplayedDates.Start || this.previousDisplayedDates.End != allDisplayedDates.End) { this.DiscardCachedMonthDateAreaLocations(); this.previousDisplayedDates.Start = allDisplayedDates.Start; this.previousDisplayedDates.End = allDisplayedDates.End; } Point monthDateAreaLocation = this.GetMonthDateAreaLocation(selectionDate); if (monthDateAreaLocation.IsEmpty) return Rectangle.Empty; DayOfWeek monthFirstDayOfWeek = (new DateTime(selectionDate.Year, selectionDate.Month, 1)).DayOfWeek; int dayOfWeekAdjust = (int)monthFirstDayOfWeek - (int)this.calendarFirstDayOfWeek; if (dayOfWeekAdjust < 0) dayOfWeekAdjust += 7; int row = (selectionDate.Day - 1 + dayOfWeekAdjust) / 7; int col = (selectionDate.Day - 1 + dayOfWeekAdjust) % 7; row += adjust1Week; return new Rectangle( monthDateAreaLocation.X + col * this.dayCellWidth, monthDateAreaLocation.Y + row * this.dayCellHeight, this.dayCellWidth, this.dayCellHeight); } ///  /// Cached calendar location from the last lookup. ///  private Point[] cachedMonthDateAreaLocation = new Point[13]; ///  /// Discard the cached month locations when calendar moves. ///  private void DiscardCachedMonthDateAreaLocations() { for (int i = 0; i < 13; i++) this.cachedMonthDateAreaLocation[i] = Point.Empty; } ///  /// Gets the graphics location (x,y point) of the top left of the /// calendar date area for the month containing the specified date. ///  private Point GetMonthDateAreaLocation(DateTime selectionDate) { Point monthDateAreaLocation = this.cachedMonthDateAreaLocation[selectionDate.Month]; HitTestInfo hitInfo; if (!monthDateAreaLocation.IsEmpty && (hitInfo = this.HitTest(monthDateAreaLocation.X, monthDateAreaLocation.Y + this.dayCellHeight)) .HitArea == HitArea.Date && hitInfo.Time.Year == selectionDate.Year && hitInfo.Time.Month == selectionDate.Month) { // Use previously cached lookup return monthDateAreaLocation; } else { // Assume the worst (Error: empty) monthDateAreaLocation = this.cachedMonthDateAreaLocation[selectionDate.Month] = Point.Empty; Point monthDataAreaPoint = this.GetMonthDateAreaMiddle(selectionDate); if (monthDataAreaPoint.IsEmpty) return Point.Empty; // Move left from the middle to find the left edge of the Date area monthDateAreaLocation.X = monthDataAreaPoint.X--; HitTestInfo hitInfo1, hitInfo2; while ((hitInfo1 = this.HitTest(monthDataAreaPoint.X, monthDataAreaPoint.Y)) .HitArea == HitArea.Date && hitInfo1.Time.Month == selectionDate.Month || (hitInfo2 = this.HitTest(monthDataAreaPoint.X, monthDataAreaPoint.Y + this.dayCellHeight)) .HitArea == HitArea.Date && hitInfo2.Time.Month == selectionDate.Month) { monthDateAreaLocation.X = monthDataAreaPoint.X--; if (monthDateAreaLocation.X < 0) return Point.Empty; // Error: bail } // Move up from the last column to find the top edge of the Date area int monthLastDayOfWeekX = monthDateAreaLocation.X + (this.dayCellWidth * 7 * 13) / 14; monthDateAreaLocation.Y = monthDataAreaPoint.Y--; while (this.HitTest(monthLastDayOfWeekX, monthDataAreaPoint.Y).HitArea == HitArea.Date) { monthDateAreaLocation.Y = monthDataAreaPoint.Y--; if (monthDateAreaLocation.Y < 0) return Point.Empty; // Error: bail } // Got it this.cachedMonthDateAreaLocation[selectionDate.Month] = monthDateAreaLocation; return monthDateAreaLocation; } } ///  /// Paranoid fudge/wobble of the GetMonthDateAreaMiddle in case /// our first estimate to hit the month misses. /// (Needed? perhaps not.) ///  private static Point[] searchSpiral = { new Point( 0, 0), new Point(-1,+1), new Point(+1,+1), new Point(+1,-1), new Point(-1,-1), new Point(-2,+2), new Point(+2,+2), new Point(+2,-2), new Point(-2,-2) }; ///  /// Gets a point somewhere inside the calendar date area of /// the month containing the given selection date. ///  /// The point returned will be HitArea.Date, and match the year and /// month of the selection date; otherwise it will be Point.Empty. private Point GetMonthDateAreaMiddle(DateTime selectionDate) { // Iterate over all displayed months, and a search spiral (needed? perhaps not) for (int dimX = 1; dimX <= this.CalendarDimensions.Width; dimX++) { for (int dimY = 1; dimY <= this.CalendarDimensions.Height; dimY++) { foreach (Point search in searchSpiral) { Point monthDateAreaMiddle = new Point( ((dimX - 1) * 2 + 1) * this.Width / (2 * this.CalendarDimensions.Width) + this.dayCellWidth * search.X, ((dimY - 1) * 2 + 1) * this.Height / (2 * this.CalendarDimensions.Height) + this.dayCellHeight * search.Y); HitTestInfo hitInfo = this.HitTest(monthDateAreaMiddle); if (hitInfo.HitArea == HitArea.Date) { // Got the Date Area of the month if (hitInfo.Time.Year == selectionDate.Year && hitInfo.Time.Month == selectionDate.Month) { // For the correct month return monthDateAreaMiddle; } else { // Keep looking in the other months break; } } } } } return Point.Empty; // Error: not found } ///  /// When this MonthCalendar is resized, recalculate the size of a day cell. ///  private void OnSizeChanged(object sender, EventArgs e) { // Discard previous cached Month Area Location DiscardCachedMonthDateAreaLocations(); this.dayCellWidth = this.dayCellHeight = 0; // Without this, the repaint sometimes does not happen... this.Invalidate(); // Determine Y offset of days area int middle = this.Width / (2 * this.CalendarDimensions.Width); int dateAreaTop = 0; while (this.HitTest(middle, dateAreaTop).HitArea != HitArea.PrevMonthDate && this.HitTest(middle, dateAreaTop).HitArea != HitArea.Date) { dateAreaTop++; if (dateAreaTop > this.ClientSize.Height) return; // Error: bail } // Determine height of a single day box int dayCellHeight = 1; DateTime dayCellTime = this.HitTest(middle, dateAreaTop).Time; while (this.HitTest(middle, dateAreaTop + dayCellHeight).Time == dayCellTime) { dayCellHeight++; } // Determine X offset of days area middle = this.Height / (2 * this.CalendarDimensions.Height); int dateAreaLeft = 0; while (this.HitTest(dateAreaLeft, middle).HitArea != HitArea.Date) { dateAreaLeft++; if (dateAreaLeft > this.ClientSize.Width) return; // Error: bail } // Determine width of a single day box int dayCellWidth = 1; dayCellTime = this.HitTest(dateAreaLeft, middle).Time; while (this.HitTest(dateAreaLeft + dayCellWidth, middle).Time == dayCellTime) { dayCellWidth++; } // Record day box size and actual first day of the month used this.calendarFirstDayOfWeek = dayCellTime.DayOfWeek; this.dayCellWidth = dayCellWidth; this.dayCellHeight = dayCellHeight; } } 

这是一个在显示超过一个月时工作的版本(CalendarDimensions!=(1,1)),并修复了一些其他问题:

 ///  /// When Visual Styles are enabled on Windows XP, the MonthCalendar.SelectionRange /// does not paint correctly when more than one date is selected. /// See: http://msdn.microsoft.com/en-us/library/5d1acks5(VS.80).aspx /// "Additionally, if you enable visual styles on some controls, the control might display incorrectly /// in certain situations. These include the MonthCalendar control with a selection range set... /// This class fixes that problem. ///  /// Author: Mark Cranness public class FixVisualStylesMonthCalendar : System.Windows.Forms.MonthCalendar { ///  /// The width of a single cell (date) in the calendar. ///  private int dayCellWidth; ///  /// The height of a single cell (date) in the calendar. ///  private int dayCellHeight; ///  /// The calendar first day of the week actually used. ///  private DayOfWeek calendarFirstDayOfWeek; ///  /// Only repaint when VisualStyles enabled on Windows XP. ///  private bool repaintSelectionRange = false; ///  /// A MonthCalendar class that fixes SelectionRange painting problems /// on Windows XP when Visual Styles is enabled. ///  public FixVisualStylesMonthCalendar() { if (Application.RenderWithVisualStyles && Environment.OSVersion.Version < new Version(6, 0)) { // If Visual Styles are enabled, and XP, then fix-up the painting of SelectionRange this.repaintSelectionRange = true; this.OnSizeChanged(this, EventArgs.Empty); this.SizeChanged += new EventHandler(this.OnSizeChanged); } } ///  /// The WM_PAINT message is sent to make a request to paint a portion of a window. ///  public const int WM_PAINT = 0x000F; ///  /// Override WM_PAINT to repaint the selection range. ///  [System.Diagnostics.DebuggerStepThroughAttribute()] protected override void WndProc(ref Message m) { base.WndProc(ref m); if (m.Msg == WM_PAINT && !this.DesignMode && this.repaintSelectionRange) { // MonthCalendar is ControlStyles.UserPaint=false => Paint event is not raised this.RepaintSelectionRange(ref m); } } ///  /// Repaint the SelectionRange. ///  private void RepaintSelectionRange(ref Message m) { using (Graphics graphics = this.CreateGraphics()) using (Brush backBrush = new SolidBrush(graphics.GetNearestColor(this.BackColor))) using (Brush selectionBrush = new SolidBrush(graphics.GetNearestColor(SystemColors.ActiveCaption))) { Rectangle todayFrame = Rectangle.Empty; // For each day in SelectionRange... for (DateTime selectionDate = this.SelectionStart; selectionDate <= this.SelectionEnd; selectionDate = selectionDate.AddDays(1)) { Rectangle selectionDayRectangle = this.GetSelectionDayRectangle(selectionDate); if (selectionDayRectangle.IsEmpty) continue; if (selectionDate.Date == this.TodayDate) { todayFrame = selectionDayRectangle; } // Paint as 'selected' a little smaller than the whole rectangle Rectangle highlightRectangle = Rectangle.Inflate(selectionDayRectangle, 0, -2); if (selectionDate == this.SelectionStart) { highlightRectangle.X += 2; highlightRectangle.Width -= 2; } if (selectionDate == this.SelectionEnd) { highlightRectangle.Width -= 2; } // Paint background, selection and day-of-month text graphics.FillRectangle(backBrush, selectionDayRectangle); graphics.FillRectangle(selectionBrush, highlightRectangle); TextRenderer.DrawText( graphics, selectionDate.Day.ToString(), this.Font, selectionDayRectangle, SystemColors.ActiveCaptionText, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter); } if (this.ShowTodayCircle && !todayFrame.IsEmpty) { // Redraw the ShowTodayCircle (square) that we painted over above using (Pen redPen = new Pen(Color.Red)) { todayFrame.Width--; todayFrame.Height--; graphics.DrawRectangle(redPen, todayFrame); } } } } ///  /// When displayed dates changed, clear the cached month locations. ///  private SelectionRange previousDisplayedDates = new SelectionRange(); ///  /// Gets a graphics Rectangle for the area corresponding to a single date on the calendar. ///  private Rectangle GetSelectionDayRectangle(DateTime selectionDateTime) { // Handle the leading and trailing dates from the previous and next months SelectionRange allDisplayedDates = this.GetDisplayRange(false); SelectionRange fullMonthDates = this.GetDisplayRange(true); int adjust1Week; DateTime selectionDate = selectionDateTime.Date; if (selectionDate < allDisplayedDates.Start || selectionDate > allDisplayedDates.End) { // Selection Date is not displayed on calendar return Rectangle.Empty; } else if (selectionDate < fullMonthDates.Start) { // Selection Date is trailing from the previous partial month selectionDate = selectionDate.AddDays(7); adjust1Week = -1; } else if (selectionDate > fullMonthDates.End) { // Selection Date is leading from the next partial month selectionDate = selectionDate.AddDays(-14); adjust1Week = +2; } else { // A mainline date adjust1Week = 0; } // Discard cached month locations when calendar moves if (this.previousDisplayedDates.Start != allDisplayedDates.Start || this.previousDisplayedDates.End != allDisplayedDates.End) { this.DiscardCachedMonthDateAreaLocations(); this.previousDisplayedDates.Start = allDisplayedDates.Start; this.previousDisplayedDates.End = allDisplayedDates.End; } Point monthDateAreaLocation = this.GetMonthDateAreaLocation(selectionDate); if (monthDateAreaLocation.IsEmpty) return Rectangle.Empty; DayOfWeek monthFirstDayOfWeek = (new DateTime(selectionDate.Year, selectionDate.Month, 1)).DayOfWeek; int dayOfWeekAdjust = (int)monthFirstDayOfWeek - (int)this.calendarFirstDayOfWeek; if (dayOfWeekAdjust < 0) dayOfWeekAdjust += 7; int row = (selectionDate.Day - 1 + dayOfWeekAdjust) / 7; int col = (selectionDate.Day - 1 + dayOfWeekAdjust) % 7; row += adjust1Week; return new Rectangle( monthDateAreaLocation.X + col * this.dayCellWidth, monthDateAreaLocation.Y + row * this.dayCellHeight, this.dayCellWidth, this.dayCellHeight); } ///  /// Cached calendar location from the last lookup. ///  private Point[] cachedMonthDateAreaLocation = new Point[13]; ///  /// Discard the cached month locations when calendar moves. ///  private void DiscardCachedMonthDateAreaLocations() { for (int i = 0; i < 13; i++) this.cachedMonthDateAreaLocation[i] = Point.Empty; } ///  /// Gets the graphics location (x,y point) of the top left of the /// calendar date area for the month containing the specified date. ///  private Point GetMonthDateAreaLocation(DateTime selectionDate) { Point monthDateAreaLocation = this.cachedMonthDateAreaLocation[selectionDate.Month]; HitTestInfo hitInfo; if (!monthDateAreaLocation.IsEmpty && (hitInfo = this.HitTest(monthDateAreaLocation.X, monthDateAreaLocation.Y + this.dayCellHeight)) .HitArea == HitArea.Date && hitInfo.Time.Year == selectionDate.Year && hitInfo.Time.Month == selectionDate.Month) { // Use previously cached lookup return monthDateAreaLocation; } else { // Assume the worst (Error: empty) monthDateAreaLocation = this.cachedMonthDateAreaLocation[selectionDate.Month] = Point.Empty; Point monthDataAreaPoint = this.GetMonthDateAreaMiddle(selectionDate); if (monthDataAreaPoint.IsEmpty) return Point.Empty; // Move left from the middle to find the left edge of the Date area monthDateAreaLocation.X = monthDataAreaPoint.X--; HitTestInfo hitInfo1, hitInfo2; while ((hitInfo1 = this.HitTest(monthDataAreaPoint.X, monthDataAreaPoint.Y)) .HitArea == HitArea.Date && hitInfo1.Time.Month == selectionDate.Month || (hitInfo2 = this.HitTest(monthDataAreaPoint.X, monthDataAreaPoint.Y + this.dayCellHeight)) .HitArea == HitArea.Date && hitInfo2.Time.Month == selectionDate.Month) { monthDateAreaLocation.X = monthDataAreaPoint.X--; if (monthDateAreaLocation.X < 0) return Point.Empty; // Error: bail } // Move up from the last column to find the top edge of the Date area int monthLastDayOfWeekX = monthDateAreaLocation.X + (this.dayCellWidth * 7 * 13) / 14; monthDateAreaLocation.Y = monthDataAreaPoint.Y--; while (this.HitTest(monthLastDayOfWeekX, monthDataAreaPoint.Y).HitArea == HitArea.Date) { monthDateAreaLocation.Y = monthDataAreaPoint.Y--; if (monthDateAreaLocation.Y < 0) return Point.Empty; // Error: bail } // Got it this.cachedMonthDateAreaLocation[selectionDate.Month] = monthDateAreaLocation; return monthDateAreaLocation; } } ///  /// Paranoid fudge/wobble of the GetMonthDateAreaMiddle in case /// our first estimate to hit the month misses. /// (Needed? perhaps not.) ///  private static Point[] searchSpiral = { new Point( 0, 0), new Point(-1,+1), new Point(+1,+1), new Point(+1,-1), new Point(-1,-1), new Point(-2,+2), new Point(+2,+2), new Point(+2,-2), new Point(-2,-2) }; ///  /// Gets a point somewhere inside the calendar date area of /// the month containing the given selection date. ///  /// The point returned will be HitArea.Date, and match the year and /// month of the selection date; otherwise it will be Point.Empty. private Point GetMonthDateAreaMiddle(DateTime selectionDate) { // Iterate over all displayed months, and a search spiral (needed? perhaps not) for (int dimX = 1; dimX <= this.CalendarDimensions.Width; dimX++) { for (int dimY = 1; dimY <= this.CalendarDimensions.Height; dimY++) { foreach (Point search in searchSpiral) { Point monthDateAreaMiddle = new Point( ((dimX - 1) * 2 + 1) * this.Width / (2 * this.CalendarDimensions.Width) + this.dayCellWidth * search.X, ((dimY - 1) * 2 + 1) * this.Height / (2 * this.CalendarDimensions.Height) + this.dayCellHeight * search.Y); HitTestInfo hitInfo = this.HitTest(monthDateAreaMiddle); if (hitInfo.HitArea == HitArea.Date) { // Got the Date Area of the month if (hitInfo.Time.Year == selectionDate.Year && hitInfo.Time.Month == selectionDate.Month) { // For the correct month return monthDateAreaMiddle; } else { // Keep looking in the other months break; } } } } } return Point.Empty; // Error: not found } ///  /// When this MonthCalendar is resized, recalculate the size of a day cell. ///  private void OnSizeChanged(object sender, EventArgs e) { // Discard previous cached Month Area Location DiscardCachedMonthDateAreaLocations(); this.dayCellWidth = this.dayCellHeight = 0; // Without this, the repaint sometimes does not happen... this.Invalidate(); // Determine Y offset of days area int middle = this.Width / (2 * this.CalendarDimensions.Width); int dateAreaTop = 0; while (this.HitTest(middle, dateAreaTop).HitArea != HitArea.PrevMonthDate && this.HitTest(middle, dateAreaTop).HitArea != HitArea.Date) { dateAreaTop++; if (dateAreaTop > this.ClientSize.Height) return; // Error: bail } // Determine height of a single day box int dayCellHeight = 1; DateTime dayCellTime = this.HitTest(middle, dateAreaTop).Time; while (this.HitTest(middle, dateAreaTop + dayCellHeight).Time == dayCellTime) { dayCellHeight++; } // Determine X offset of days area middle = this.Height / (2 * this.CalendarDimensions.Height); int dateAreaLeft = 0; while (this.HitTest(dateAreaLeft, middle).HitArea != HitArea.Date) { dateAreaLeft++; if (dateAreaLeft > this.ClientSize.Width) return; // Error: bail } // Determine width of a single day box int dayCellWidth = 1; dayCellTime = this.HitTest(dateAreaLeft, middle).Time; while (this.HitTest(dateAreaLeft + dayCellWidth, middle).Time == dayCellTime) { dayCellWidth++; } // Record day box size and actual first day of the month used this.calendarFirstDayOfWeek = dayCellTime.DayOfWeek; this.dayCellWidth = dayCellWidth; this.dayCellHeight = dayCellHeight; } } 

我的测试显示Windows 7没有绘画问题,我预计Vista也不会,所以这只是尝试修复Windows XP。

你可以试试这段代码:

 Dim StartDate As Date = New DateTime(2011, 9, 21) Dim EndDate As Date = New DateTime(2011, 9, 25) MonthCalendar1.SelectionRange = New SelectionRange(StartDate, EndDate) 

欲获得更多信息:

http://www.authorcode.com/how-to-select-a-range-of-dates-in-the-monthcalendar-control/

http://www.authorcode.com/how-to-enable-windows-xp-visual-styles-of-net-application/