MonthCalendar控件的选择范围EnableVisualStyles? [英] MonthCalendar control selection range with EnableVisualStyles?

查看:596
本文介绍了MonthCalendar控件的选择范围EnableVisualStyles?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using the MonthCalendar control and want to programmatically select a date range. When I do so the control doesn't paint properly if Application.EnableVisualStyles() has been called. This is a known issue according to MSDN.

使用的MonthCalendar视觉   风格启用将导致选择   范围MonthCalendar控件来   不正确绘制   (来源:的http://msdn.microsoft.com/en-us/library/system.windows.forms.monthcalendar.aspx)

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 没有解决?这似乎使这一特定的控制完全无用了广泛的应用,并从我的角度来看,相当明显的监督。

Is there really no fix for this other than not calling EnableVisualStyles? This seems to make that particular control entirely useless for a range of applications and a rather glaring oversight from my perspective.

推荐答案

我发现马克Cranness的code以上的小问题:在具有外观风格完全禁用XP系统,Application.RenderWithVisualStyles然后设置为False,即使当Application.EnableVisualStyles()被调用。

I found a small problem in Mark Cranness's code above: On XP systems that have visual styles disabled entirely, Application.RenderWithVisualStyles is then set to False even when Application.EnableVisualStyles() is called.

因此​​,定制油漆code不运行在所有在这种情况下。为了解决这个问题,我改变了FixVisualStylesMonthCalendar构造函数的第一行

So the custom paint code does not run at all in that case. To fix it, I changed the first line of the FixVisualStylesMonthCalendar constructor to

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

整个code是在这个答案的底部。

Entire code is at the bottom of this answer.

我找不到任何方式对答案本身进行评论。积分低于code去原作者 - (如果他或任何人都可以验证这个答案并更新它,我会很乐意删除它)

I could not find any way to comment on the answer itself. Credits for below code go to the original author - (If he or anyone can verify this answer and update it I would be happy to remove this one)

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

    }
}

这篇关于MonthCalendar控件的选择范围EnableVisualStyles?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆