/// <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(); }
/// <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; } } } }