/// <summary>
        /// Initializes a new instance of the <see cref="MonthCalendarMonth"/> class.
        /// </summary>
        /// <param name="enhancedMonthCal">
        /// The <see cref="Calendar"/> which hosts the <see cref="MonthCalendarMonth"/> instance.
        /// </param>
        /// <param name="date">
        /// The date that represents the month and year which is displayed in this instance.
        /// </param>
        public MonthCalendarMonth(EnhancedMonthCalendar enhancedMonthCal, DateTime date)
        {
            this.Calendar = enhancedMonthCal;
            this.Date = date;
            this._location = new Point(0, 0);

            MonthCalendarDate dt =
                new MonthCalendarDate(enhancedMonthCal.CultureCalendar, date).FirstOfMonth.GetFirstDayInWeek(enhancedMonthCal.FormatProvider);

            List<MonthCalendarDay> dayList = new List<MonthCalendarDay>();
            List<MonthCalendarWeek> weekList = new List<MonthCalendarWeek>();

            int dayAdjust = 0;

            while (dt.AddDays(dayAdjust).DayOfWeek != enhancedMonthCal.FormatProvider.FirstDayOfWeek)
            {
                dayAdjust++;
            }

            int d = dayAdjust != 0 ? 8 - dayAdjust : 0;

            for (int i = dayAdjust; i < 42 + dayAdjust; i++, dt = dt.AddDays(1))
            {
                MonthCalendarDay day = new MonthCalendarDay(this, dt.Date);

                dayList.Add(day);

                if (day.Visible)
                {
                    if (this._firstVisibleDate == DateTime.MinValue)
                    {
                        this._firstVisibleDate = dt.Date;
                    }

                    if (!day.TrailingDate)
                    {
                        this._lastVisibleDate = dt.Date;
                    }
                }

                if (i == dayAdjust || ((i - d) % 7) == 0)
                {
                    DateTime weekEnd = dt.GetEndDateOfWeek(enhancedMonthCal.FormatProvider).Date;

                    int weekNumEnd = DateMethods.GetWeekOfYear(enhancedMonthCal.Culture, enhancedMonthCal.CultureCalendar, weekEnd);

                    weekList.Add(new MonthCalendarWeek(this, weekNumEnd, dt.Date, weekEnd));
                }

                if (dt.Date == enhancedMonthCal.CultureCalendar.MaxSupportedDateTime.Date)
                {
                    break;
                }
            }

            this.Days = dayList.ToArray();
            this.Weeks = weekList.ToArray();
        }
        /// <summary>
        /// Handles clicks in the year menu.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The event data.
        /// </param>
        private void YearClick(object sender, EventArgs e)
        {
            DateTime currentMonthYear = (DateTime)this.yearMenu.Tag;

            int yearClicked = (int)((ToolStripMenuItem)sender).Tag;

            MonthCalendarDate dt = new MonthCalendarDate(this.CultureCalendar, currentMonthYear);

            if (dt.Year != yearClicked)
            {
                MonthCalendarDate newStartDate =
                    new MonthCalendarDate(this.CultureCalendar, new DateTime(yearClicked, dt.Month, 1, this.CultureCalendar)).AddMonths(
                        -this.GetIndex(currentMonthYear));

                if (this.SetStartDate(newStartDate.Date))
                {
                    this.UpdateMonths();

                    this.RaiseDateChanged();

                    this.Focus();

                    this.Refresh();
                }
            }
        }
        /// <summary>
        /// Sets the start date.
        /// </summary>
        /// <param name="start">
        /// The start date.
        /// </param>
        /// <returns>
        /// true if <paramref name="start"/> is valid; false otherwise.
        /// </returns>
        private bool SetStartDate(DateTime start)
        {
            if (start < DateTime.MinValue.Date || start > DateTime.MaxValue.Date)
            {
                return false;
            }

            DayOfWeek firstDayOfWeek = this._formatProvider.FirstDayOfWeek;
            MonthCalendarDate dt = new MonthCalendarDate(this.CultureCalendar, this._maxDate);

            if (start > this._maxDate)
            {
                start = dt.AddMonths(1 - this.Months.Length).FirstOfMonth.Date;
            }

            if (start < this._minDate)
            {
                start = this._minDate;
            }

            dt = new MonthCalendarDate(this.CultureCalendar, start);
            int length = this.Months != null ? this.Months.Length - 1 : 0;

            while (dt.Date > this._minDate && dt.Day != 1)
            {
                dt = dt.AddDays(-1);
            }

            MonthCalendarDate endDate = dt.AddMonths(length);
            MonthCalendarDate endDateDay = endDate.AddDays(endDate.DaysInMonth - 1 - (endDate.Day - 1));

            if (endDate.Date >= this._maxDate || endDateDay.Date >= this._maxDate)
            {
                dt = new MonthCalendarDate(this.CultureCalendar, this._maxDate).AddMonths(-length).FirstOfMonth;
            }

            this.viewStart = dt.Date;

            while (dt.Date > this.CultureCalendar.MinSupportedDateTime.Date && dt.DayOfWeek != firstDayOfWeek)
            {
                dt = dt.AddDays(-1);
            }

            this.realStart = dt.Date;
            return true;
        }
        /// <summary>
        /// Sets the selection range for the specified <see cref="MonthCalendarSelectionMode"/>.
        /// </summary>
        /// <param name="selMode">
        /// The <see cref="MonthCalendarSelectionMode"/> value to set the selection range for.
        /// </param>
        private void SetSelectionRange(MonthCalendarSelectionMode selMode)
        {
            switch (selMode)
            {
                case MonthCalendarSelectionMode.Day:
                    {
                        this.selectionEnd = this.selectionStart;
                        break;
                    }

                case MonthCalendarSelectionMode.WorkWeek:
                case MonthCalendarSelectionMode.FullWeek:
                    {
                        MonthCalendarDate dt = new MonthCalendarDate(this.CultureCalendar, this.selectionStart).GetFirstDayInWeek(
                            this._formatProvider);
                        this.selectionStart = dt.Date;
                        this.selectionEnd = dt.AddDays(6).Date;

                        break;
                    }

                case MonthCalendarSelectionMode.Month:
                    {
                        MonthCalendarDate dt = new MonthCalendarDate(this.CultureCalendar, this.selectionStart).FirstOfMonth;
                        this.selectionStart = dt.Date;
                        this.selectionEnd = dt.AddMonths(1).AddDays(-1).Date;

                        break;
                    }
            }
        }
        /// <summary>
        /// Handles clicks in the month menu.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The event data.
        /// </param>
        private void MonthClick(object sender, EventArgs e)
        {
            MonthCalendarDate currentMonthYear = new MonthCalendarDate(this.CultureCalendar, (DateTime)this.monthMenu.Tag);

            int monthClicked = (int)((ToolStripMenuItem)sender).Tag;

            if (currentMonthYear.Month != monthClicked)
            {
                MonthCalendarDate dt = new MonthCalendarDate(
                    this.CultureCalendar,
                    new DateTime(currentMonthYear.Year, monthClicked, 1, this.CultureCalendar));
                DateTime newStartDate = dt.AddMonths(-this.GetIndex(currentMonthYear.Date)).Date;

                if (this.SetStartDate(newStartDate))
                {
                    this.UpdateMonths();
                    this.RaiseDateChanged();
                    this.Focus();
                    this.Refresh();
                }
            }
        }
        /// <summary>
        /// Checks if the <paramref name="newSelectionDate"/> is within bounds of the <paramref name="baseDate"/>
        /// and the <see cref="MaxSelectionCount"/>.
        /// </summary>
        /// <param name="baseDate">
        /// The base date from where to check.
        /// </param>
        /// <param name="newSelectionDate">
        /// The new selection date.
        /// </param>
        /// <returns>
        /// A valid new selection date if valid parameters, otherwise <c>DateTime.MinValue</c>.
        /// </returns>
        private DateTime GetSelectionDate(DateTime baseDate, DateTime newSelectionDate)
        {
            if (this.maxSelectionCount == 0 || baseDate == DateTime.MinValue)
            {
                return newSelectionDate;
            }

            if (baseDate >= this.CultureCalendar.MinSupportedDateTime && newSelectionDate >= this.CultureCalendar.MinSupportedDateTime
                && baseDate <= this.CultureCalendar.MaxSupportedDateTime && newSelectionDate <= this.CultureCalendar.MaxSupportedDateTime)
            {
                int days = (baseDate - newSelectionDate).Days;

                if (Math.Abs(days) >= this.maxSelectionCount)
                {
                    newSelectionDate =
                        new MonthCalendarDate(this.CultureCalendar, baseDate).AddDays(
                            days < 0 ? this.maxSelectionCount - 1 : 1 - this.maxSelectionCount).Date;
                }

                return newSelectionDate;
            }

            return DateTime.MinValue;
        }
        /// <summary>
        /// Gets the new date for the specified scroll direction.
        /// </summary>
        /// <param name="up">
        /// true for scrolling upwards, false otherwise.
        /// </param>
        /// <returns>
        /// The new start date.
        /// </returns>
        private DateTime GetNewScrollDate(bool up)
        {
            if ((this._lastVisibleDate == this._maxDate && !up) || (this.Months[0].FirstVisibleDate == this._minDate && up))
            {
                return this.viewStart;
            }

            MonthCalendarDate dt = new MonthCalendarDate(this.CultureCalendar, this.viewStart);

            int monthsToAdd = (this.scrollChange == 0
                                   ? Math.Max((this.calendarDimensions.Width * this.calendarDimensions.Height) - 1, 1)
                                   : this.scrollChange) * (up ? -1 : 1);

            int length = this.Months == null ? Math.Max(1, this.calendarDimensions.Width * this.calendarDimensions.Height) : this.Months.Length;

            MonthCalendarDate newStartMonthDate = dt.AddMonths(monthsToAdd);
            MonthCalendarDate lastMonthDate = newStartMonthDate.AddMonths(length - 1);
            MonthCalendarDate lastMonthEndDate = lastMonthDate.AddDays(lastMonthDate.DaysInMonth - 1 - lastMonthDate.Day);

            if (newStartMonthDate.Date < this._minDate)
            {
                newStartMonthDate = new MonthCalendarDate(this.CultureCalendar, this._minDate);
            }
            else if (lastMonthEndDate.Date >= this._maxDate || lastMonthDate.Date >= this._maxDate)
            {
                MonthCalendarDate maxdt = new MonthCalendarDate(this.CultureCalendar, this._maxDate).FirstOfMonth;
                newStartMonthDate = maxdt.AddMonths(1 - length);
            }

            return newStartMonthDate.Date;
        }
        /// <summary>
        /// Calculates the various sizes of a single month view and the global size of the control.
        /// </summary>
        /// <param name="changeDimension">
        /// The change Dimension.
        /// </param>
        private void CalculateSize(bool changeDimension)
        {
            // if already calculating - return
            if (this._inUpdate)
            {
                return;
            }

            this._inUpdate = true;

            using (Graphics g = this.CreateGraphics())
            {
                // get sizes for different elements of the calendar
                SizeF daySize = g.MeasureString("30", this.Font);
                SizeF weekNumSize = g.MeasureString("59", this.Font);

                MonthCalendarDate date = new MonthCalendarDate(this.CultureCalendar, this.viewStart);

                SizeF monthNameSize = g.MeasureString(this._formatProvider.GetMonthName(date.Year, date.Month), this.headerFont);
                SizeF yearStringSize = g.MeasureString(this.viewStart.ToString("yyyy"), this.headerFont);
                SizeF footerStringSize = g.MeasureString(this.viewStart.ToShortDateString(), this.footerFont);

                // calculate the header height
                this._headerHeight = Math.Max((int)Math.Max(monthNameSize.Height + 3, yearStringSize.Height) + 1, 15);

                // calculate the width of a single day
                this._dayWidth = Math.Max(12, (int)daySize.Width + 1) + 5;

                // calculate the height of a single day
                this._dayHeight = Math.Max(Math.Max(12, (int)weekNumSize.Height + 1), (int)daySize.Height + 1) + 2;

                // calculate the height of the footer
                this._footerHeight = Math.Max(20, (int)footerStringSize.Height + 2);

                // calculate the width of the week number header
                this._weekNumberWidth = this.showWeekHeader ? Math.Max(12, (int)weekNumSize.Width + 1) + 2 : 0;

                // set minimal height of the day name header
                this._dayNameHeight = this.dayHeaderFont.Height + 10;

                // loop through all day names
                foreach (string str in DateMethods.GetDayNames(this._formatProvider, this.useShortestDayNames ? 2 : 1))
                {
                    // get the size of the name
                    SizeF dayNameSize = g.MeasureString(str, this.dayHeaderFont);

                    // adjust the width of the day and the day name header height
                    this._dayWidth = Math.Max(this._dayWidth, (int)dayNameSize.Width + 1);
                    this._dayNameHeight = Math.Max(this._dayNameHeight, (int)dayNameSize.Height + 1);
                }

                // calculate the width and height of a MonthCalendarMonth element
                this._monthWidth = this._weekNumberWidth + (this._dayWidth * 7) + 2;
                this._monthHeight = this._headerHeight + this._dayNameHeight + (this._dayHeight * 6) + 2;

                if (changeDimension)
                {
                    // calculate the dimension of the control
                    int calWidthDim = Math.Max(1, this.Width / this._monthWidth);
                    int calHeightDim = Math.Max(1, this.Height / this._monthHeight);

                    // set the dimensions
                    this.CalendarDimensions = new Size(calWidthDim, calHeightDim);
                }

                // set the width and height of the control
                this.Height = (this._monthHeight * this.calendarDimensions.Height) + (this.showFooter ? this._footerHeight : 0) + this.Padding.Top
                              + this.Padding.Bottom;
                this.Width = this._monthWidth * this.calendarDimensions.Width + this.Padding.Top + this.Padding.Bottom;

                // calculate the footer bounds
                this._footerRect = new Rectangle(1, this.Height - this._footerHeight - 1, this.Width - 2, this._footerHeight);

                // update the months
                this.UpdateMonths();
            }

            this._inUpdate = false;
            this.Refresh();
        }
        /// <summary>
        /// Processes a dialog key.
        /// </summary>
        /// <param name="keyData">
        /// One of the <see cref="Keys"/> value that represents the key to process.
        /// </param>
        /// <returns>
        /// true if the key was processed by the control; otherwise, false.
        /// </returns>
        protected override bool ProcessDialogKey(Keys keyData)
        {
            if (this.daySelectionMode != MonthCalendarSelectionMode.Day)
            {
                return base.ProcessDialogKey(keyData);
            }

            MonthCalendarDate dt = new MonthCalendarDate(this.cultureCalendar, this.selectionStart);
            bool retValue = false;

            if (keyData == Keys.Left)
            {
                this.selectionStart = dt.AddDays(-1).Date;
                retValue = true;
            }
            else if (keyData == Keys.Right)
            {
                this.selectionStart = dt.AddDays(1).Date;
                retValue = true;
            }
            else if (keyData == Keys.Up)
            {
                this.selectionStart = dt.AddDays(-7).Date;
                retValue = true;
            }
            else if (keyData == Keys.Down)
            {
                this.selectionStart = dt.AddDays(7).Date;
                retValue = true;
            }

            if (retValue)
            {
                if (this.selectionStart < this._minDate)
                {
                    this.selectionStart = this._minDate;
                }
                else if (this.selectionStart > this._maxDate)
                {
                    this.selectionStart = this._maxDate;
                }

                this.SetSelectionRange(this.daySelectionMode);
                this.EnsureSeletedDateIsVisible();
                this.RaiseInternalDateSelected();
                this.Invalidate();
                return true;
            }

            return base.ProcessDialogKey(keyData);
        }
        /// <summary>Updates the shown months.</summary>
        public void UpdateMonths()
        {
            int x = this.Padding.Left, y = this.Padding.Top, index = 0;
            int calWidthDim = this.calendarDimensions.Width;
            int calHeightDim = this.calendarDimensions.Height;

            List<MonthCalendarMonth> monthList = new List<MonthCalendarMonth>();
            MonthCalendarDate dt = new MonthCalendarDate(this.CultureCalendar, this.viewStart);

            if (dt.GetEndDateOfWeek(this._formatProvider).Month != dt.Month)
            {
                dt = dt.GetEndDateOfWeek(this._formatProvider).FirstOfMonth;
            }

            if (this.UseRTL)
            {
                x = this._monthWidth * (calWidthDim - 1);

                for (int i = 0; i < calHeightDim; i++)
                {
                    for (int j = calWidthDim - 1; j >= 0; j--)
                    {
                        if (dt.Date >= this._maxDate)
                        {
                            break;
                        }

                        monthList.Add(new MonthCalendarMonth(this, dt.Date) { Location = new Point(x, y), Index = index++ });

                        x -= this._monthWidth;
                        dt = dt.AddMonths(1);
                    }

                    x = this._monthWidth * (calWidthDim - 1);
                    y += this._monthHeight;
                }
            }
            else
            {
                for (int i = 0; i < calHeightDim; i++)
                {
                    for (int j = 0; j < calWidthDim; j++)
                    {
                        if (dt.Date >= this._maxDate)
                        {
                            break;
                        }

                        monthList.Add(new MonthCalendarMonth(this, dt.Date) { Location = new Point(x, y), Index = index++ });

                        x += this._monthWidth;
                        dt = dt.AddMonths(1);
                    }

                    x = 0;
                    y += this._monthHeight;
                }
            }

            this._lastVisibleDate = monthList[monthList.Count - 1].LastVisibleDate;

            this.Months = monthList.ToArray();
        }
Exemple #11
0
        /// <summary>
        /// Calculates the proportions of this instance of <see cref="MonthCalendarMonth"/>.
        /// </summary>
        /// <param name="loc">
        /// The top left corner of the month.
        /// </param>
        private void CalculateProportions(Point loc)
        {
            // set title bounds
            this.TitleBounds = new Rectangle(loc, this.Calendar.HeaderSize);

            // set helper variables
            bool useRTL = this.Calendar.UseRTL;
            int adjustX = this.Calendar.WeekNumberSize.Width;
            int dayWidth = this.Calendar.DaySize.Width;
            int dayHeight = this.Calendar.DaySize.Height;
            int weekRectAdjust = 0;

            // if RTL mode
            if (useRTL)
            {
                // set new values
                weekRectAdjust = dayWidth * 7;
                adjustX = 0;
            }

            // calculate day names header bounds
            this.DayNamesBounds = new Rectangle(new Point(loc.X + adjustX, loc.Y + this.TitleBounds.Height), this.Calendar.DayNamesSize);

            // calculate week number header bounds
            Rectangle weekNumberRect = new Rectangle(
                loc.X + weekRectAdjust,
                loc.Y + this.TitleBounds.Height + this.DayNamesBounds.Height,
                this.Calendar.WeekNumberSize.Width,
                dayHeight);

            // save week header bounds
            Rectangle weekBounds = weekNumberRect;

            // calculate month body bounds
            Rectangle monthRect = new Rectangle(
                loc.X + adjustX,
                loc.Y + this.TitleBounds.Height + this.DayNamesBounds.Height,
                dayWidth * 7,
                dayHeight * 6);

            // get start position at where to draw
            int startX = monthRect.X;
            adjustX = dayWidth;

            // if in RTL mode adjust start position and advancing width
            if (useRTL)
            {
                startX = monthRect.Right - dayWidth;
                adjustX = -dayWidth;
            }

            int x = startX;
            int y = monthRect.Y;
            int j = 0;

            if (this.Days.Length > 0
                && new MonthCalendarDate(this.Calendar.CultureCalendar, this.Days[0].Date).DayOfWeek != this.Calendar.FormatProvider.FirstDayOfWeek)
            {
                DayOfWeek currentDayOfWeek = this.Calendar.FormatProvider.FirstDayOfWeek;
                DayOfWeek dayOfWeek = new MonthCalendarDate(this.Calendar.CultureCalendar, this.Days[0].Date).DayOfWeek;

                while (currentDayOfWeek != dayOfWeek)
                {
                    x += adjustX;

                    if ((j + 1) % 7 == 0)
                    {
                        x = startX;
                        y += dayHeight;
                    }

                    int nextDay = (int)currentDayOfWeek + 1;

                    if (nextDay > 6)
                    {
                        nextDay = 0;
                    }

                    currentDayOfWeek = (DayOfWeek)nextDay;

                    j++;
                }
            }

            // loop through all possible 42 days
            for (int i = 0; i < this.Days.Length; i++, j++)
            {
                // set bounds of the day
                this.Days[i].Bounds = new Rectangle(x, y, dayWidth, dayHeight);

                // if at the beginning of a row
                if (i % 7 == 0)
                {
                    // check if any day in the week is visible
                    bool visible = this.Days[i].Visible || this.Days[Math.Min(i + 6, this.Days.Length - 1)].Visible;

                    // set the week number header element bounds and if it's visible
                    this.Weeks[i / 7].Bounds = weekNumberRect;
                    this.Weeks[i / 7].Visible = visible;

                    // adjust bounds of week header
                    if (visible)
                    {
                        weekBounds = Rectangle.Union(weekBounds, weekNumberRect);
                    }

                    // adjust top position of the week number header bounds
                    weekNumberRect.Y += dayHeight;
                }

                // adjust left position of the next day
                x += adjustX;

                // if last day in the row
                if ((j + 1) % 7 == 0)
                {
                    // reset to start drawing position
                    x = startX;

                    // and advance a row
                    y += dayHeight;
                }
            }

            // adjust the month body height and top position
            monthRect.Y = weekBounds.Y;
            monthRect.Height = weekBounds.Height;

            // set month body bounds
            this.MonthBounds = monthRect;

            // set week number header bounds
            this.WeekBounds = weekBounds;
        }
        /// <summary>
        /// Raises the <see cref="System.Windows.Forms.Control.MouseMove"/> event.
        /// </summary>
        /// <param name="e">
        /// A <see cref="MouseEventArgs"/> that contains the event data.
        /// </param>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (e.Location == this.mouseLocation)
            {
                return;
            }

            this.mouseLocation = e.Location;

            // backup and reset mouse move flags
            this.mouseMoveFlags.BackupAndReset();

            // perform hit test
            MonthCalendarHitTest hit = this.HitTest(e.Location);

            if (e.Button == MouseButtons.Left)
            {
                // if selection started - only in manual selection mode
                if (this.selectionStarted)
                {
                    // if selection started with hit type Day and mouse is over new date
                    if (hit.Type == MonthCalendarHitType.Day && this.currentHitType == MonthCalendarHitType.Day
                        && this.currentMoveBounds != hit.Bounds)
                    {
                        this.currentMoveBounds = hit.Bounds;

                        // set new selection end
                        this.SelectionEnd = hit.Date;
                    }
                    else if (hit.Type == MonthCalendarHitType.Week && this.currentHitType == MonthCalendarHitType.Week)
                    {
                        // set indicator that a week header element is selected
                        this.mouseMoveFlags.WeekHeader = true;

                        // get new end date
                        DateTime endDate = new MonthCalendarDate(this.CultureCalendar, hit.Date).AddDays(6).Date;

                        // if new week header element
                        if (this.currentMoveBounds != hit.Bounds)
                        {
                            this.currentMoveBounds = hit.Bounds;

                            // check if selection is switched
                            if (this.selectionStart == this._selectionStartRange.End)
                            {
                                // are we after the original end date?
                                if (endDate > this.selectionStart)
                                {
                                    // set original start date
                                    this.selectionStart = this._selectionStartRange.Start;

                                    // set new end date
                                    this.SelectionEnd = endDate;
                                }
                                else
                                {
                                    // going backwards - set new "end" date - it's now the start date
                                    this.SelectionEnd = hit.Date;
                                }
                            }
                            else
                            {
                                // we are after the start date
                                if (endDate > this.selectionStart)
                                {
                                    // set end date
                                    this.SelectionEnd = endDate;
                                }
                                else
                                {
                                    // switch start and end
                                    this.selectionStart = this._selectionStartRange.End;
                                    this.SelectionEnd = hit.Date;
                                }
                            }
                        }
                    }
                }
                else
                {
                    switch (hit.Type)
                    {
                        case MonthCalendarHitType.MonthName:
                            {
                                this.mouseMoveFlags.MonthName = hit.Date;
                                this.mouseMoveFlags.HeaderDate = hit.Date;
                                this.Invalidate(hit.InvalidateBounds);
                                break;
                            }

                        case MonthCalendarHitType.MonthYear:
                            {
                                this.mouseMoveFlags.Year = hit.Date;
                                this.mouseMoveFlags.HeaderDate = hit.Date;
                                this.Invalidate(hit.InvalidateBounds);
                                break;
                            }

                        case MonthCalendarHitType.Header:
                            {
                                this.mouseMoveFlags.HeaderDate = hit.Date;
                                this.Invalidate(hit.InvalidateBounds);
                                break;
                            }

                        case MonthCalendarHitType.Arrow:
                            {
                                bool useRTL = this.UseRTL;

                                if (this._leftArrowRect.Contains(e.Location))
                                {
                                    this.mouseMoveFlags.LeftArrow = !useRTL;
                                    this.mouseMoveFlags.RightArrow = useRTL;
                                    this.mouseMoveFlags.HeaderDate = this.Months[0].Date;
                                }
                                else
                                {
                                    this.mouseMoveFlags.LeftArrow = useRTL;
                                    this.mouseMoveFlags.RightArrow = !useRTL;
                                    this.mouseMoveFlags.HeaderDate = this.Months[this.calendarDimensions.Width - 1].Date;
                                }

                                this.Invalidate(hit.InvalidateBounds);
                                break;
                            }

                        case MonthCalendarHitType.Footer:
                            {
                                this.mouseMoveFlags.Footer = true;
                                this.Invalidate(hit.InvalidateBounds);
                                break;
                            }

                        default:
                            {
                                this.Invalidate();
                                break;
                            }
                    }
                }
            }
            else if (e.Button == MouseButtons.None)
            {
                // no mouse button is pressed
                // set flags and invalidate corresponding region
                switch (hit.Type)
                {
                    case MonthCalendarHitType.Day:
                        {
                            this.mouseMoveFlags.Day = hit.Date;
                            var bold = this.GetBoldedDates().Contains(hit.Date)
                                       || this._boldDatesCollection.Exists(d => d.Value.Date == hit.Date.Date);
                            this.OnActiveDateChanged(new ActiveDateChangedEventArgs(hit.Date, bold));
                            this.InvalidateMonth(hit.Date, true);
                            break;
                        }

                    case MonthCalendarHitType.Week:
                        {
                            this.mouseMoveFlags.WeekHeader = true;

                            break;
                        }

                    case MonthCalendarHitType.MonthName:
                        {
                            this.mouseMoveFlags.MonthName = hit.Date;
                            this.mouseMoveFlags.HeaderDate = hit.Date;
                            break;
                        }

                    case MonthCalendarHitType.MonthYear:
                        {
                            this.mouseMoveFlags.Year = hit.Date;
                            this.mouseMoveFlags.HeaderDate = hit.Date;
                            break;
                        }

                    case MonthCalendarHitType.Header:
                        {
                            this.mouseMoveFlags.HeaderDate = hit.Date;
                            break;
                        }

                    case MonthCalendarHitType.Arrow:
                        {
                            bool useRTL = this.UseRTL;

                            if (this._leftArrowRect.Contains(e.Location))
                            {
                                this.mouseMoveFlags.LeftArrow = !useRTL;
                                this.mouseMoveFlags.RightArrow = useRTL;

                                this.mouseMoveFlags.HeaderDate = this.Months[0].Date;
                            }
                            else if (this._rightArrowRect.Contains(e.Location))
                            {
                                this.mouseMoveFlags.LeftArrow = useRTL;
                                this.mouseMoveFlags.RightArrow = !useRTL;

                                this.mouseMoveFlags.HeaderDate = this.Months[this.calendarDimensions.Width - 1].Date;
                            }

                            break;
                        }

                    case MonthCalendarHitType.Footer:
                        {
                            this.mouseMoveFlags.Footer = true;
                            break;
                        }
                }

                // if left arrow RequestState changed
                if (this.mouseMoveFlags.LeftArrowChanged)
                {
                    this.Invalidate(this.UseRTL ? this._rightArrowRect : this._leftArrowRect);

                    this.Update();
                }

                // if right arrow RequestState changed
                if (this.mouseMoveFlags.RightArrowChanged)
                {
                    this.Invalidate(this.UseRTL ? this._leftArrowRect : this._rightArrowRect);

                    this.Update();
                }

                // if header RequestState changed
                if (this.mouseMoveFlags.HeaderDateChanged)
                {
                    this.Invalidate();
                }
                else if (this.mouseMoveFlags.MonthNameChanged || this.mouseMoveFlags.YearChanged)
                {
                    // if RequestState of month name or year in header changed
                    SelectionRange range1 = new SelectionRange(this.mouseMoveFlags.MonthName, this.mouseMoveFlags.Backup.MonthName);

                    SelectionRange range2 = new SelectionRange(this.mouseMoveFlags.Year, this.mouseMoveFlags.Backup.Year);

                    this.Invalidate(this.Months[this.GetIndex(range1.End)].TitleBounds);

                    if (range1.End != range2.End)
                    {
                        this.Invalidate(this.Months[this.GetIndex(range2.End)].TitleBounds);
                    }
                }

                // if day RequestState changed
                if (this.mouseMoveFlags.DayChanged)
                {
                    // invalidate current day
                    this.InvalidateMonth(this.mouseMoveFlags.Day, false);

                    // invalidate last day
                    this.InvalidateMonth(this.mouseMoveFlags.Backup.Day, false);
                }

                // if footer RequestState changed
                if (this.mouseMoveFlags.FooterChanged)
                {
                    this.Invalidate(this._footerRect);
                }
            }

            // if mouse is over a week header, change cursor
            if (this.mouseMoveFlags.WeekHeaderChanged)
            {
                this.Cursor = this.mouseMoveFlags.WeekHeader ? Cursors.UpArrow : Cursors.Default;
            }
        }
        /// <summary>
        /// Raises the <see cref="System.Windows.Forms.Control.MouseDown"/> event.
        /// </summary>
        /// <param name="e">
        /// A <see cref="MouseEventArgs"/> that contains the event data.
        /// </param>
        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            this.Focus();
            this.Capture = true;

            // reset the selection range where selection started
            this._selectionStartRange = null;
            if (e.Button == MouseButtons.Left)
            {
                MonthCalendarHitTest hit = this.HitTest(e.Location); // perform hit test
                this.currentMoveBounds = hit.Bounds; // set current bounds
                this.currentHitType = hit.Type; // set current hit type

                switch (hit.Type)
                {
                    case MonthCalendarHitType.Day:
                        {
                            // save old selection range
                            SelectionRange oldRange = this.SelectionRange;

                            if (!this.extendSelection || this.daySelectionMode != MonthCalendarSelectionMode.Manual)
                            {
                                // clear all selection ranges
                                this.selectionRanges.Clear();
                            }

                            switch (this.daySelectionMode)
                            {
                                case MonthCalendarSelectionMode.Day:
                                    {
                                        this.OnDateClicked(new DateEventArgs(hit.Date));

                                        // only single days are selectable
                                        if (this.selectionStart != hit.Date)
                                        {
                                            this.SelectionStart = hit.Date;
                                            this.RaiseDateSelected();
                                        }

                                        break;
                                    }

                                case MonthCalendarSelectionMode.WorkWeek:
                                    {
                                        // only single work week is selectable
                                        // get first day of week
                                        DateTime firstDay =
                                            new MonthCalendarDate(this.CultureCalendar, hit.Date).GetFirstDayInWeek(this._formatProvider).Date;

                                        // get work days
                                        List<DayOfWeek> workDays = DateMethods.GetWorkDays(this.nonWorkDays);

                                        // reset selection start and end
                                        this.selectionEnd = DateTime.MinValue;
                                        this.selectionStart = DateTime.MinValue;

                                        // current range
                                        SelectionRange currentRange = null;

                                        // build selection ranges for work days
                                        for (int i = 0; i < 7; i++)
                                        {
                                            DateTime toAdd = firstDay.AddDays(i);

                                            if (workDays.Contains(toAdd.DayOfWeek))
                                            {
                                                if (currentRange == null)
                                                {
                                                    currentRange = new SelectionRange(DateTime.MinValue, DateTime.MinValue);
                                                }

                                                if (currentRange.Start == DateTime.MinValue)
                                                {
                                                    currentRange.Start = toAdd;
                                                }
                                                else
                                                {
                                                    currentRange.End = toAdd;
                                                }
                                            }
                                            else if (currentRange != null)
                                            {
                                                this.selectionRanges.Add(currentRange);
                                                currentRange = null;
                                            }
                                        }

                                        if (this.selectionRanges.Count >= 1)
                                        {
                                            // set first selection range
                                            this.SelectionRange = this.selectionRanges[0];
                                            this.selectionRanges.RemoveAt(0);

                                            // if selection range changed, raise event
                                            if (this.SelectionRange != oldRange)
                                            {
                                                this.RaiseDateSelected();
                                            }
                                        }
                                        else
                                        {
                                            this.Refresh();
                                        }

                                        break;
                                    }

                                case MonthCalendarSelectionMode.FullWeek:
                                    {
                                        // only a full week is selectable
                                        // get selection start and end
                                        MonthCalendarDate dt =
                                            new MonthCalendarDate(this.CultureCalendar, hit.Date).GetFirstDayInWeek(this._formatProvider);
                                        this.selectionStart = dt.Date;
                                        this.selectionEnd = dt.GetEndDateOfWeek(this._formatProvider).Date;

                                        // if range changed, raise event
                                        if (this.SelectionRange != oldRange)
                                        {
                                            this.RaiseDateSelected();
                                            this.Refresh();
                                        }

                                        break;
                                    }

                                case MonthCalendarSelectionMode.Month:
                                    {
                                        // only a full month is selectable
                                        MonthCalendarDate dt = new MonthCalendarDate(this.CultureCalendar, hit.Date).FirstOfMonth;

                                        // get selection start and end
                                        this.selectionStart = dt.Date;
                                        this.selectionEnd = dt.AddMonths(1).AddDays(-1).Date;

                                        // if range changed, raise event
                                        if (this.SelectionRange != oldRange)
                                        {
                                            this.RaiseDateSelected();
                                            this.Refresh();
                                        }

                                        break;
                                    }

                                case MonthCalendarSelectionMode.Manual:
                                    {
                                        if (this.extendSelection)
                                        {
                                            var range = this.selectionRanges.Find(r => hit.Date >= r.Start && hit.Date <= r.End);
                                            if (range != null)
                                            {
                                                this.selectionRanges.Remove(range);
                                            }
                                        }

                                        // manual mode - selection ends when user is releasing the left mouse button
                                        this.selectionStarted = true;
                                        this._backupRange = this.SelectionRange;
                                        this.selectionEnd = DateTime.MinValue;
                                        this.SelectionStart = hit.Date;
                                        break;
                                    }
                            }

                            break;
                        }

                    case MonthCalendarHitType.Week:
                        {
                            this.selectionRanges.Clear();

                            if (this.maxSelectionCount > 6 || this.maxSelectionCount == 0)
                            {
                                this._backupRange = this.SelectionRange;
                                this.selectionStarted = true;
                                this.selectionEnd = new MonthCalendarDate(this.CultureCalendar, hit.Date).GetEndDateOfWeek(this._formatProvider).Date;
                                this.SelectionStart = hit.Date;
                                this._selectionStartRange = this.SelectionRange;
                            }

                            break;
                        }

                    case MonthCalendarHitType.MonthName:
                        {
                            this._monthSelected = hit.Date;
                            this.mouseMoveFlags.HeaderDate = hit.Date;

                            this.Invalidate(hit.InvalidateBounds);
                            this.Update();

                            this.monthMenu.Tag = hit.Date;
                            this.UpdateMonthMenu(this.CultureCalendar.GetYear(hit.Date));

                            this._showingMenu = true;

                            // show month menu
                            this.monthMenu.Show(this, hit.Bounds.Right, e.Location.Y);
                            break;
                        }

                    case MonthCalendarHitType.MonthYear:
                        {
                            this._yearSelected = hit.Date;
                            this.mouseMoveFlags.HeaderDate = hit.Date;

                            this.Invalidate(hit.InvalidateBounds);
                            this.Update();

                            this.UpdateYearMenu(this.CultureCalendar.GetYear(hit.Date));

                            this.yearMenu.Tag = hit.Date;

                            this._showingMenu = true;

                            // show year menu
                            this.yearMenu.Show(this, hit.Bounds.Right, e.Location.Y);

                            break;
                        }

                    case MonthCalendarHitType.Arrow:
                        {
                            // an arrow was pressed
                            // set new start date
                            if (this.SetStartDate(hit.Date))
                            {
                                // update months
                                this.UpdateMonths();

                                // raise event
                                this.RaiseDateChanged();

                                this.mouseMoveFlags.HeaderDate = this._leftArrowRect.Contains(e.Location)
                                                                     ? this.Months[0].Date
                                                                     : this.Months[this.calendarDimensions.Width - 1].Date;

                                this.Refresh();
                            }

                            break;
                        }

                    case MonthCalendarHitType.Footer:
                        {
                            // footer was pressed
                            this.selectionRanges.Clear();

                            bool raiseDateChanged = false;

                            SelectionRange range = this.SelectionRange;

                            // determine if date changed event has to be raised
                            if (DateTime.Today < this.Months[0].FirstVisibleDate || DateTime.Today > this._lastVisibleDate)
                            {
                                // set new start date
                                if (this.SetStartDate(DateTime.Today))
                                {
                                    // update months
                                    this.UpdateMonths();

                                    raiseDateChanged = true;
                                }
                                else
                                {
                                    break;
                                }
                            }

                            // set new selection start and end values
                            this.selectionStart = DateTime.Today;
                            this.selectionEnd = DateTime.Today;

                            this.SetSelectionRange(this.daySelectionMode);

                            this.OnDateClicked(new DateEventArgs(DateTime.Today));

                            // raise events if necessary
                            if (range != this.SelectionRange)
                            {
                                this.RaiseDateSelected();
                            }

                            if (raiseDateChanged)
                            {
                                this.RaiseDateChanged();
                            }

                            this.Refresh();
                            break;
                        }

                    case MonthCalendarHitType.Header:
                        {
                            // header was pressed
                            this.Invalidate(hit.Bounds);
                            this.Update();
                            break;
                        }
                }
            }
        }