Диапазон выбора элемента управления MonthCalendar с EnableVisualStyles?

Я использую элемент управления MonthCalendar и хочу программно выбрать диапазон дат. Когда я это делаю, элемент управления не отрисовывается должным образом, если был вызван Application.EnableVisualStyles(). Согласно MSDN, это известная проблема.

Using the MonthCalendar with visual styles enabled will cause a selection range for the MonthCalendar control to not paint correctly (from: http://msdn.microsoft.com/en-us/library/system.windows.forms.monthcalendar.aspx)

Неужели для этого нет другого решения, кроме как не вызывать EnableVisualStyles? Похоже, это делает этот конкретный элемент управления совершенно бесполезным для ряда приложений и, с моей точки зрения, довольно явный надзор.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
8 343
4

Ответы 4

В поисках решения той же проблемы я впервые столкнулся с этим вопросом здесь, но позже я обнаружил запись в блоге Ник Андерссон. что я нашел очень полезным. Вот что я сделал из примера Ника:

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;
        }
    }
}

Кажется, работает, но onMouseLeave прямоугольник перекрашивается в цвет по умолчанию ... Что нужно изменить, чтобы прямоугольник оставался постоянным? Было бы неплохо узнать.

SiL3NC3 21.10.2016 19:28

Это не должно быть так сложно. Microsoft необходимо обновить свои элементы управления и предоставить людям современный пользовательский интерфейс, а не тот, который был 20 лет назад.

John Lamberis 02.04.2020 22:13

Вот версия, которая работает, когда отображается более одного месяца (CalendarDimensions! = (1,1)), а также исправляет некоторые другие проблемы:

/// <summary>
/// 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.
/// </summary>
/// <remarks>Author: Mark Cranness</remarks>
public class FixVisualStylesMonthCalendar : System.Windows.Forms.MonthCalendar {

    /// <summary>
    /// The width of a single cell (date) in the calendar.
    /// </summary>
    private int dayCellWidth;
    /// <summary>
    /// The height of a single cell (date) in the calendar.
    /// </summary>
    private int dayCellHeight;

    /// <summary>
    /// The calendar first day of the week actually used.
    /// </summary>
    private DayOfWeek calendarFirstDayOfWeek;

    /// <summary>
    /// Only repaint when VisualStyles enabled on Windows XP.
    /// </summary>
    private bool repaintSelectionRange = false;

    /// <summary>
    /// A MonthCalendar class that fixes SelectionRange painting problems 
    /// on Windows XP when Visual Styles is enabled.
    /// </summary>
    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);

        }
    }

    /// <summary>
    /// The WM_PAINT message is sent to make a request to paint a portion of a window.
    /// </summary>
    public const int WM_PAINT = 0x000F;

    /// <summary>
    /// Override WM_PAINT to repaint the selection range.
    /// </summary>
    [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);
        }
    }

    /// <summary>
    /// Repaint the SelectionRange.
    /// </summary>
    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);
                }
            }

        }
    }

    /// <summary>
    /// When displayed dates changed, clear the cached month locations.
    /// </summary>
    private SelectionRange previousDisplayedDates = new SelectionRange();

    /// <summary>
    /// Gets a graphics Rectangle for the area corresponding to a single date on the calendar.
    /// </summary>
    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);

    }

    /// <summary>
    /// Cached calendar location from the last lookup.
    /// </summary>
    private Point[] cachedMonthDateAreaLocation = new Point[13];

    /// <summary>
    /// Discard the cached month locations when calendar moves.
    /// </summary>
    private void DiscardCachedMonthDateAreaLocations() {
        for (int i = 0; i < 13; i++) this.cachedMonthDateAreaLocation[i] = Point.Empty;
    }

    /// <summary>
    /// Gets the graphics location (x,y point) of the top left of the
    /// calendar date area for the month containing the specified date.
    /// </summary>
    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;

        }
    }

    /// <summary>
    /// Paranoid fudge/wobble of the GetMonthDateAreaMiddle in case 
    /// our first estimate to hit the month misses.
    /// (Needed? perhaps not.)
    /// </summary>
    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)
    };

    /// <summary>
    /// Gets a point somewhere inside the calendar date area of
    /// the month containing the given selection date.
    /// </summary>
    /// <remarks>The point returned will be HitArea.Date, and match the year and
    /// month of the selection date; otherwise it will be Point.Empty.</remarks>
    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

    }

    /// <summary>
    /// When this MonthCalendar is resized, recalculate the size of a day cell.
    /// </summary>
    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.

Я обнаружил небольшую проблему в приведенном выше коде Марка Крэннесса: в системах XP, в которых визуальные стили полностью отключены, Application.RenderWithVisualStyles затем устанавливается в значение False, даже когда вызывается Application.EnableVisualStyles ().

Таким образом, пользовательский код рисования в этом случае вообще не запускается. Чтобы исправить это, я изменил первую строку конструктора FixVisualStylesMonthCalendar на

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

Весь код находится внизу этого ответа.

Я не нашел способа прокомментировать сам ответ. Кредиты для приведенного ниже кода переходят к исходному автору - (если он или кто-либо может проверить этот ответ и обновить его, я был бы счастлив удалить этот)

/// <summary>
/// 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.
/// </summary>
/// <remarks>Author: Mark Cranness - PatronBase Limited.</remarks>
public class FixVisualStylesMonthCalendar : System.Windows.Forms.MonthCalendar
{

    /// <summary>
    /// The width of a single cell (date) in the calendar.
    /// </summary>
    private int dayCellWidth;
    /// <summary>
    /// The height of a single cell (date) in the calendar.
    /// </summary>
    private int dayCellHeight;

    /// <summary>
    /// The calendar first day of the week actually used.
    /// </summary>
    private DayOfWeek calendarFirstDayOfWeek;

    /// <summary>
    /// Only repaint when VisualStyles enabled on Windows XP.
    /// </summary>
    private bool repaintSelectionRange = false;

    /// <summary>
    /// A MonthCalendar class that fixes SelectionRange painting problems 
    /// on Windows XP when Visual Styles is enabled.
    /// </summary>
    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);
        }
    }

    /// <summary>
    /// The WM_PAINT message is sent to make a request to paint a portion of a window.
    /// </summary>
    public const int WM_PAINT = 0x000F;

    /// <summary>
    /// Override WM_PAINT to repaint the selection range.
    /// </summary>
    [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);
        }
    }

    /// <summary>
    /// Repaint the SelectionRange.
    /// </summary>
    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);
                }
            }

        }
    }

    /// <summary>
    /// When displayed dates changed, clear the cached month locations.
    /// </summary>
    private SelectionRange previousDisplayedDates = new SelectionRange();

    /// <summary>
    /// Gets a graphics Rectangle for the area corresponding to a single date on the calendar.
    /// </summary>
    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);

    }

    /// <summary>
    /// Cached calendar location from the last lookup.
    /// </summary>
    private Point[] cachedMonthDateAreaLocation = new Point[13];

    /// <summary>
    /// Discard the cached month locations when calendar moves.
    /// </summary>
    private void DiscardCachedMonthDateAreaLocations()
    {
        for (int i = 0; i < 13; i++) this.cachedMonthDateAreaLocation[i] = Point.Empty;
    }

    /// <summary>
    /// Gets the graphics location (x,y point) of the top left of the
    /// calendar date area for the month containing the specified date.
    /// </summary>
    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;

        }
    }

    /// <summary>
    /// Paranoid fudge/wobble of the GetMonthDateAreaMiddle in case 
    /// our first estimate to hit the month misses.
    /// (Needed? perhaps not.)
    /// </summary>
    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)
};

    /// <summary>
    /// Gets a point somewhere inside the calendar date area of
    /// the month containing the given selection date.
    /// </summary>
    /// <remarks>The point returned will be HitArea.Date, and match the year and
    /// month of the selection date; otherwise it will be Point.Empty.</remarks>
    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

    }

    /// <summary>
    /// When this MonthCalendar is resized, recalculate the size of a day cell.
    /// </summary>
    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;

    }
}

вы можете попробовать этот код:

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/

Другие вопросы по теме