1 //------------------------------------------------------------------------------
2 // <copyright file="Calendar.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 namespace System.Web.UI.WebControls {
8 using System.Threading;
9 using System.Globalization;
10 using System.ComponentModel;
14 using System.Web.Util;
15 using System.Collections;
16 using System.ComponentModel.Design;
21 using System.Reflection;
26 /// <para>Displays a one-month calendar and allows the user to
27 /// view and select a specific day, week, or month.</para>
30 ControlValueProperty("SelectedDate", typeof(DateTime), "1/1/0001"),
31 DataBindingHandler("System.Web.UI.Design.WebControls.CalendarDataBindingHandler, " + AssemblyRef.SystemDesign),
32 DefaultEvent("SelectionChanged"),
33 DefaultProperty("SelectedDate"),
34 Designer("System.Web.UI.Design.WebControls.CalendarDesigner, " + AssemblyRef.SystemDesign),
35 SupportsEventValidation
37 public class Calendar : WebControl, IPostBackEventHandler {
39 private static readonly object EventDayRender = new object();
40 private static readonly object EventSelectionChanged = new object();
41 private static readonly object EventVisibleMonthChanged = new object();
43 private TableItemStyle titleStyle;
44 private TableItemStyle nextPrevStyle;
45 private TableItemStyle dayHeaderStyle;
46 private TableItemStyle selectorStyle;
47 private TableItemStyle dayStyle;
48 private TableItemStyle otherMonthDayStyle;
49 private TableItemStyle todayDayStyle;
50 private TableItemStyle selectedDayStyle;
51 private TableItemStyle weekendDayStyle;
52 private string defaultButtonColorText;
54 private static readonly Color DefaultForeColor = Color.Black;
55 private Color defaultForeColor;
57 private ArrayList dateList;
58 private SelectedDatesCollection selectedDates;
59 private System.Globalization.Calendar threadCalendar;
60 private DateTime minSupportedDate;
61 private DateTime maxSupportedDate;
63 private bool threadCalendarInitialized;
66 private const string SELECT_RANGE_COMMAND = "R";
67 private const string NAVIGATE_MONTH_COMMAND = "V";
69 private static DateTime baseDate = new DateTime(2000, 1, 1);
71 private const int STYLEMASK_DAY = 16;
72 private const int STYLEMASK_UNIQUE = 15;
73 private const int STYLEMASK_SELECTED = 8;
74 private const int STYLEMASK_TODAY = 4;
75 private const int STYLEMASK_OTHERMONTH = 2;
76 private const int STYLEMASK_WEEKEND = 1;
77 private const string ROWBEGINTAG = "<tr>";
78 private const string ROWENDTAG = "</tr>";
80 // Cache commonly used strings. This improves performance and memory usage.
81 private const int cachedNumberMax = 31;
82 private static readonly string[] cachedNumbers = new string [] {
83 "0", "1", "2", "3", "4", "5", "6",
84 "7", "8", "9", "10", "11", "12", "13",
85 "14", "15", "16", "17", "18", "19", "20",
86 "21", "22", "23", "24", "25", "26", "27",
87 "28", "29", "30", "31",
92 /// <para>Initializes a new instance of the <see cref='System.Web.UI.WebControls.Calendar'/> class.</para>
94 public Calendar() : base(HtmlTextWriterTag.Table) {
101 WebCategory("Accessibility"),
102 WebSysDescription(SR.Calendar_Caption)
104 public virtual string Caption {
106 string s = (string)ViewState["Caption"];
107 return (s != null) ? s : String.Empty;
110 ViewState["Caption"] = value;
116 DefaultValue(TableCaptionAlign.NotSet),
117 WebCategory("Accessibility"),
118 WebSysDescription(SR.WebControl_CaptionAlign)
120 public virtual TableCaptionAlign CaptionAlign {
122 object o = ViewState["CaptionAlign"];
123 return (o != null) ? (TableCaptionAlign)o : TableCaptionAlign.NotSet;
126 if ((value < TableCaptionAlign.NotSet) ||
127 (value > TableCaptionAlign.Right)) {
128 throw new ArgumentOutOfRangeException("value");
130 ViewState["CaptionAlign"] = value;
136 /// <para>Gets or sets the amount of space between cells.</para>
139 WebCategory("Layout"),
141 WebSysDescription(SR.Calendar_CellPadding)
143 public int CellPadding {
145 object o = ViewState["CellPadding"];
146 return((o == null) ? 2 : (int)o);
150 throw new ArgumentOutOfRangeException("value");
152 ViewState["CellPadding"] = value;
158 /// <para>Gets or sets the amount of space between the contents of a cell
159 /// and the cell's border.</para>
162 WebCategory("Layout"),
164 WebSysDescription(SR.Calendar_CellSpacing)
166 public int CellSpacing {
168 object o = ViewState["CellSpacing"];
169 return((o == null) ? 0 : (int)o);
173 throw new ArgumentOutOfRangeException("value");
175 ViewState["CellSpacing"] = (int)value;
181 /// <para> Gets the style property of the day-of-the-week header. This property is read-only.</para>
184 WebCategory("Styles"),
185 WebSysDescription(SR.Calendar_DayHeaderStyle),
186 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
187 NotifyParentProperty(true),
188 PersistenceMode(PersistenceMode.InnerProperty)
190 public TableItemStyle DayHeaderStyle {
192 if (dayHeaderStyle == null) {
193 dayHeaderStyle = new TableItemStyle();
194 if (IsTrackingViewState)
195 ((IStateManager)dayHeaderStyle).TrackViewState();
197 return dayHeaderStyle;
203 /// <para>Gets or sets
204 /// the format for the names of days.</para>
207 WebCategory("Appearance"),
208 DefaultValue(DayNameFormat.Short),
209 WebSysDescription(SR.Calendar_DayNameFormat)
211 public DayNameFormat DayNameFormat {
213 object dnf = ViewState["DayNameFormat"];
214 return((dnf == null) ? DayNameFormat.Short : (DayNameFormat)dnf);
217 if (value < DayNameFormat.Full || value > DayNameFormat.Shortest) {
218 throw new ArgumentOutOfRangeException("value");
220 ViewState["DayNameFormat"] = value;
226 /// <para> Gets the style properties for the days. This property is read-only.</para>
229 WebCategory("Styles"),
231 WebSysDescription(SR.Calendar_DayStyle),
232 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
233 NotifyParentProperty(true),
234 PersistenceMode(PersistenceMode.InnerProperty)
236 public TableItemStyle DayStyle {
238 if (dayStyle == null) {
239 dayStyle = new TableItemStyle();
240 if (IsTrackingViewState)
241 ((IStateManager)dayStyle).TrackViewState();
250 /// or sets the day of the week to display in the calendar's first
254 WebCategory("Appearance"),
255 DefaultValue(FirstDayOfWeek.Default),
256 WebSysDescription(SR.Calendar_FirstDayOfWeek)
258 public FirstDayOfWeek FirstDayOfWeek {
260 object o = ViewState["FirstDayOfWeek"];
261 return((o == null) ? FirstDayOfWeek.Default : (FirstDayOfWeek)o);
264 if (value < FirstDayOfWeek.Sunday || value > FirstDayOfWeek.Default) {
265 throw new ArgumentOutOfRangeException("value");
268 ViewState["FirstDayOfWeek"] = value;
274 /// <para>Gets or sets the text shown for the next month
275 /// navigation hyperlink if the <see cref='System.Web.UI.WebControls.Calendar.ShowNextPrevMonth'/> property is set to
276 /// <see langword='true'/>.</para>
280 WebCategory("Appearance"),
281 DefaultValue(">"),
282 WebSysDescription(SR.Calendar_NextMonthText)
284 public string NextMonthText {
286 object s = ViewState["NextMonthText"];
287 return((s == null) ? ">" : (String) s);
290 ViewState["NextMonthText"] = value;
296 /// <para>Gets or sets the format of the next and previous month hyperlinks in the
300 WebCategory("Appearance"),
301 DefaultValue(NextPrevFormat.CustomText),
302 WebSysDescription(SR.Calendar_NextPrevFormat)
304 public NextPrevFormat NextPrevFormat {
306 object npf = ViewState["NextPrevFormat"];
307 return((npf == null) ? NextPrevFormat.CustomText : (NextPrevFormat)npf);
310 if (value < NextPrevFormat.CustomText || value > NextPrevFormat.FullMonth) {
311 throw new ArgumentOutOfRangeException("value");
313 ViewState["NextPrevFormat"] = value;
319 /// <para> Gets the style properties for the next/previous month navigators. This property is
320 /// read-only.</para>
323 WebCategory("Styles"),
324 WebSysDescription(SR.Calendar_NextPrevStyle),
325 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
326 NotifyParentProperty(true),
327 PersistenceMode(PersistenceMode.InnerProperty)
329 public TableItemStyle NextPrevStyle {
331 if (nextPrevStyle == null) {
332 nextPrevStyle = new TableItemStyle();
333 if (IsTrackingViewState)
334 ((IStateManager)nextPrevStyle).TrackViewState();
336 return nextPrevStyle;
343 /// <para>Gets the style properties for the days from the months preceding and following the current month.
344 /// This property is read-only.</para>
347 WebCategory("Styles"),
349 WebSysDescription(SR.Calendar_OtherMonthDayStyle),
350 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
351 NotifyParentProperty(true),
352 PersistenceMode(PersistenceMode.InnerProperty)
354 public TableItemStyle OtherMonthDayStyle {
356 if (otherMonthDayStyle == null) {
357 otherMonthDayStyle = new TableItemStyle();
358 if (IsTrackingViewState)
359 ((IStateManager)otherMonthDayStyle).TrackViewState();
362 return otherMonthDayStyle;
368 /// <para>Gets or sets the text shown for the previous month
369 /// navigation hyperlink if the <see cref='System.Web.UI.WebControls.Calendar.ShowNextPrevMonth'/> property is set to
370 /// <see langword='true'/>
375 WebCategory("Appearance"),
376 DefaultValue("<"),
377 WebSysDescription(SR.Calendar_PrevMonthText)
379 public string PrevMonthText {
381 object s = ViewState["PrevMonthText"];
382 return((s == null) ? "<" : (String) s);
385 ViewState["PrevMonthText"] = value;
389 public override bool SupportsDisabledAttribute {
391 return RenderingCompatibility < VersionUtil.Framework40;
396 /// <para>Gets or sets the date that is currently selected
400 Bindable(true, BindingDirection.TwoWay),
401 DefaultValue(typeof(DateTime), "1/1/0001"),
402 WebSysDescription(SR.Calendar_SelectedDate)
404 public DateTime SelectedDate {
406 if (SelectedDates.Count == 0) {
407 return DateTime.MinValue;
409 return SelectedDates[0];
412 if (value == DateTime.MinValue) {
413 SelectedDates.Clear();
416 SelectedDates.SelectRange(value, value);
423 /// <para>Gets a collection of <see cref='System.DateTime' qualify='true'/> objects representing days selected on the <see cref='System.Web.UI.WebControls.Calendar'/>. This
424 /// property is read-only.</para>
428 WebSysDescription(SR.Calendar_SelectedDates),
429 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
431 public SelectedDatesCollection SelectedDates {
433 if (selectedDates == null) {
434 if (dateList == null) {
435 dateList = new ArrayList();
437 selectedDates = new SelectedDatesCollection(dateList);
439 return selectedDates;
445 /// <para>Gets the style properties for the selected date. This property is read-only.</para>
448 WebCategory("Styles"),
450 WebSysDescription(SR.Calendar_SelectedDayStyle),
451 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
452 NotifyParentProperty(true),
453 PersistenceMode(PersistenceMode.InnerProperty)
455 public TableItemStyle SelectedDayStyle {
457 if (selectedDayStyle == null) {
458 selectedDayStyle = new TableItemStyle();
459 if (IsTrackingViewState)
460 ((IStateManager)selectedDayStyle).TrackViewState();
462 return selectedDayStyle;
468 /// <para>Gets or sets the date selection capabilities on the
469 /// <see cref='System.Web.UI.WebControls.Calendar'/>
470 /// to allow the user to select a day, week, or month.</para>
473 WebCategory("Behavior"),
474 DefaultValue(CalendarSelectionMode.Day),
475 WebSysDescription(SR.Calendar_SelectionMode)
477 public CalendarSelectionMode SelectionMode {
479 object csm = ViewState["SelectionMode"];
480 return((csm == null) ? CalendarSelectionMode.Day : (CalendarSelectionMode)csm);
483 if (value < CalendarSelectionMode.None || value > CalendarSelectionMode.DayWeekMonth) {
484 throw new ArgumentOutOfRangeException("value");
486 ViewState["SelectionMode"] = value;
492 /// <para>Gets or sets the text shown for the month selection in
493 /// the selector column if <see cref='System.Web.UI.WebControls.Calendar.SelectionMode'/> is
494 /// <see langword='CalendarSelectionMode.DayWeekMonth'/>.</para>
498 WebCategory("Appearance"),
499 DefaultValue(">>"),
500 WebSysDescription(SR.Calendar_SelectMonthText)
502 public string SelectMonthText {
504 object s = ViewState["SelectMonthText"];
505 return((s == null) ? ">>" : (String) s);
508 ViewState["SelectMonthText"] = value;
514 /// <para> Gets the style properties for the week and month selectors. This property is read-only.</para>
517 WebCategory("Styles"),
518 WebSysDescription(SR.Calendar_SelectorStyle),
519 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
520 NotifyParentProperty(true),
521 PersistenceMode(PersistenceMode.InnerProperty)
523 public TableItemStyle SelectorStyle {
525 if (selectorStyle == null) {
526 selectorStyle = new TableItemStyle();
527 if (IsTrackingViewState)
528 ((IStateManager)selectorStyle).TrackViewState();
530 return selectorStyle;
535 /// <para>Gets or sets the text shown for the week selection in
536 /// the selector column if <see cref='System.Web.UI.WebControls.Calendar.SelectionMode'/> is
537 /// <see langword='CalendarSelectionMode.DayWeek '/>or
538 /// <see langword='CalendarSelectionMode.DayWeekMonth'/>.</para>
542 WebCategory("Appearance"),
543 DefaultValue(">"),
544 WebSysDescription(SR.Calendar_SelectWeekText)
546 public string SelectWeekText {
548 object s = ViewState["SelectWeekText"];
549 return((s == null) ? ">" : (String) s);
552 ViewState["SelectWeekText"] = value;
558 /// <para>Gets or sets
559 /// a value indicating whether the days of the week are displayed.</para>
562 WebCategory("Appearance"),
564 WebSysDescription(SR.Calendar_ShowDayHeader)
566 public bool ShowDayHeader {
568 object b = ViewState["ShowDayHeader"];
569 return((b == null) ? true : (bool)b);
572 ViewState["ShowDayHeader"] = value;
578 /// <para>Gets or set
579 /// a value indicating whether days on the calendar are displayed with a border.</para>
582 WebCategory("Appearance"),
584 WebSysDescription(SR.Calendar_ShowGridLines)
586 public bool ShowGridLines {
588 object b= ViewState["ShowGridLines"];
589 return((b == null) ? false : (bool)b);
592 ViewState["ShowGridLines"] = value;
598 /// <para>Gets or sets a value indicating whether the <see cref='System.Web.UI.WebControls.Calendar'/>
599 /// displays the next and pervious month
600 /// hyperlinks in the title.</para>
603 WebCategory("Appearance"),
605 WebSysDescription(SR.Calendar_ShowNextPrevMonth)
607 public bool ShowNextPrevMonth {
609 object b = ViewState["ShowNextPrevMonth"];
610 return((b == null) ? true : (bool)b);
613 ViewState["ShowNextPrevMonth"] = value;
620 /// sets a value indicating whether the title is displayed.</para>
623 WebCategory("Appearance"),
625 WebSysDescription(SR.Calendar_ShowTitle)
627 public bool ShowTitle {
629 object b = ViewState["ShowTitle"];
630 return((b == null) ? true : (bool)b);
633 ViewState["ShowTitle"] = value;
639 /// <para>Gets or sets how the month name is formatted in the title
643 WebCategory("Appearance"),
644 DefaultValue(TitleFormat.MonthYear),
645 WebSysDescription(SR.Calendar_TitleFormat)
647 public TitleFormat TitleFormat {
649 object tf = ViewState["TitleFormat"];
650 return((tf == null) ? TitleFormat.MonthYear : (TitleFormat)tf);
653 if (value < TitleFormat.Month || value > TitleFormat.MonthYear) {
654 throw new ArgumentOutOfRangeException("value");
656 ViewState["TitleFormat"] = value;
662 /// <para>Gets the style properties of the <see cref='System.Web.UI.WebControls.Calendar'/> title. This property is
663 /// read-only.</para>
666 WebCategory("Styles"),
667 WebSysDescription(SR.Calendar_TitleStyle),
668 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
669 NotifyParentProperty(true),
670 PersistenceMode(PersistenceMode.InnerProperty),
672 public TableItemStyle TitleStyle {
674 if (titleStyle == null) {
675 titleStyle = new TableItemStyle();
676 if (IsTrackingViewState)
677 ((IStateManager)titleStyle).TrackViewState();
685 /// <para>Gets the style properties for today's date on the
686 /// <see cref='System.Web.UI.WebControls.Calendar'/>. This
687 /// property is read-only.</para>
690 WebCategory("Styles"),
692 WebSysDescription(SR.Calendar_TodayDayStyle),
693 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
694 NotifyParentProperty(true),
695 PersistenceMode(PersistenceMode.InnerProperty)
697 public TableItemStyle TodayDayStyle {
699 if (todayDayStyle == null) {
700 todayDayStyle = new TableItemStyle();
701 if (IsTrackingViewState)
702 ((IStateManager)todayDayStyle).TrackViewState();
704 return todayDayStyle;
710 /// <para>Gets or sets the value to use as today's date.</para>
714 WebSysDescription(SR.Calendar_TodaysDate),
715 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
717 public DateTime TodaysDate {
719 object o = ViewState["TodaysDate"];
720 return((o == null) ? DateTime.Today : (DateTime)o);
723 ViewState["TodaysDate"] = value.Date;
730 WebCategory("Accessibility"),
731 WebSysDescription(SR.Table_UseAccessibleHeader)
733 public virtual bool UseAccessibleHeader {
735 object o = ViewState["UseAccessibleHeader"];
736 return (o != null) ? (bool)o : true;
739 ViewState["UseAccessibleHeader"] = value;
745 /// <para>Gets or sets the date that specifies what month to display. The date can be
746 /// be any date within the month.</para>
750 DefaultValue(typeof(DateTime), "1/1/0001"),
751 WebSysDescription(SR.Calendar_VisibleDate)
753 public DateTime VisibleDate {
755 object o = ViewState["VisibleDate"];
756 return((o == null) ? DateTime.MinValue : (DateTime)o);
759 ViewState["VisibleDate"] = value.Date;
765 /// <para>Gets the style properties for the displaying weekend dates. This property is
766 /// read-only.</para>
769 WebCategory("Styles"),
770 WebSysDescription(SR.Calendar_WeekendDayStyle),
771 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
772 NotifyParentProperty(true),
773 PersistenceMode(PersistenceMode.InnerProperty)
775 public TableItemStyle WeekendDayStyle {
777 if (weekendDayStyle == null) {
778 weekendDayStyle = new TableItemStyle();
779 if (IsTrackingViewState)
780 ((IStateManager)weekendDayStyle).TrackViewState();
782 return weekendDayStyle;
789 /// <para>Occurs when each day is created in teh control hierarchy for the <see cref='System.Web.UI.WebControls.Calendar'/>.</para>
792 WebCategory("Action"),
793 WebSysDescription(SR.Calendar_OnDayRender)
795 public event DayRenderEventHandler DayRender {
797 Events.AddHandler(EventDayRender, value);
800 Events.RemoveHandler(EventDayRender, value);
808 /// <para>Occurs when the user clicks on a day, week, or month
809 /// selector and changes the <see cref='System.Web.UI.WebControls.Calendar.SelectedDate'/>.</para>
812 WebCategory("Action"),
813 WebSysDescription(SR.Calendar_OnSelectionChanged)
815 public event EventHandler SelectionChanged {
817 Events.AddHandler(EventSelectionChanged, value);
820 Events.RemoveHandler(EventSelectionChanged, value);
827 /// <para>Occurs when the
828 /// user clicks on the next or previous month <see cref='System.Web.UI.WebControls.Button'/> controls on the title.</para>
831 WebCategory("Action"),
832 WebSysDescription(SR.Calendar_OnVisibleMonthChanged)
834 public event MonthChangedEventHandler VisibleMonthChanged {
836 Events.AddHandler(EventVisibleMonthChanged, value);
839 Events.RemoveHandler(EventVisibleMonthChanged, value);
848 private void ApplyTitleStyle(TableCell titleCell, Table titleTable, TableItemStyle titleStyle) {
849 // apply affects that affect the whole background to the cell
850 if (titleStyle.BackColor != Color.Empty) {
851 titleCell.BackColor = titleStyle.BackColor;
853 if (titleStyle.BorderColor != Color.Empty) {
854 titleCell.BorderColor = titleStyle.BorderColor;
856 if (titleStyle.BorderWidth != Unit.Empty) {
857 titleCell.BorderWidth= titleStyle.BorderWidth;
859 if (titleStyle.BorderStyle != BorderStyle.NotSet) {
860 titleCell.BorderStyle = titleStyle.BorderStyle;
862 if (titleStyle.Height != Unit.Empty) {
863 titleCell.Height = titleStyle.Height;
865 if (titleStyle.VerticalAlign != VerticalAlign.NotSet) {
866 titleCell.VerticalAlign = titleStyle.VerticalAlign;
869 // apply affects that affect everything else to the table
870 if (titleStyle.CssClass.Length > 0) {
871 titleTable.CssClass = titleStyle.CssClass;
873 else if (CssClass.Length > 0) {
874 titleTable.CssClass = CssClass;
877 if (titleStyle.ForeColor != Color.Empty) {
878 titleTable.ForeColor = titleStyle.ForeColor;
880 else if (ForeColor != Color.Empty) {
881 titleTable.ForeColor = ForeColor;
883 titleTable.Font.CopyFrom(titleStyle.Font);
884 titleTable.Font.MergeWith(this.Font);
892 protected override ControlCollection CreateControlCollection() {
893 return new InternalControlCollection(this);
900 private DateTime EffectiveVisibleDate() {
901 DateTime visDate = VisibleDate;
902 if (visDate.Equals(DateTime.MinValue)) {
903 visDate = TodaysDate;
907 if (IsMinSupportedYearMonth(visDate)) {
908 return minSupportedDate;
911 return threadCalendar.AddDays(visDate, -(threadCalendar.GetDayOfMonth(visDate) - 1));
918 private DateTime FirstCalendarDay(DateTime visibleDate) {
919 DateTime firstDayOfMonth = visibleDate;
922 if (IsMinSupportedYearMonth(firstDayOfMonth)) {
923 return firstDayOfMonth;
926 int daysFromLastMonth = ((int)threadCalendar.GetDayOfWeek(firstDayOfMonth)) - NumericFirstDayOfWeek();
927 // Always display at least one day from the previous month
928 if (daysFromLastMonth <= 0) {
929 daysFromLastMonth += 7;
931 return threadCalendar.AddDays(firstDayOfMonth, -daysFromLastMonth);
937 private string GetCalendarButtonText(string eventArgument, string buttonText, string title, bool showLink, Color foreColor) {
939 StringBuilder sb = new StringBuilder();
940 sb.Append("<a href=\"");
941 sb.Append(Page.ClientScript.GetPostBackClientHyperlink(this, eventArgument, true));
943 // ForeColor needs to go on the actual link. This breaks the uplevel/downlevel rules a little bit,
944 // but it is worth doing so the day links do not change color when they go in the history on
945 // downlevel browsers. Otherwise, people get it confused with the selection mechanism.
946 sb.Append("\" style=\"color:");
947 sb.Append(foreColor.IsEmpty ? defaultButtonColorText : ColorTranslator.ToHtml(foreColor));
949 if (!String.IsNullOrEmpty(title)) {
950 sb.Append("\" title=\"");
955 sb.Append(buttonText);
957 return sb.ToString();
967 private int GetDefinedStyleMask() {
969 // Selected is always defined because it has default effects
970 int styleMask = STYLEMASK_SELECTED;
972 if (dayStyle != null && !dayStyle.IsEmpty)
973 styleMask |= STYLEMASK_DAY;
974 if (todayDayStyle != null && !todayDayStyle.IsEmpty)
975 styleMask |= STYLEMASK_TODAY;
976 if (otherMonthDayStyle != null && !otherMonthDayStyle.IsEmpty)
977 styleMask |= STYLEMASK_OTHERMONTH;
978 if (weekendDayStyle != null && !weekendDayStyle.IsEmpty)
979 styleMask |= STYLEMASK_WEEKEND;
986 private string GetMonthName(int m, bool bFull) {
988 return DateTimeFormatInfo.CurrentInfo.GetMonthName(m);
991 return DateTimeFormatInfo.CurrentInfo.GetAbbreviatedMonthName(m);
997 /// <para>Determines if a <see cref='System.Web.UI.WebControls.CalendarSelectionMode'/>
998 /// contains week selectors.</para>
1000 protected bool HasWeekSelectors(CalendarSelectionMode selectionMode) {
1001 return(selectionMode == CalendarSelectionMode.DayWeek
1002 || selectionMode == CalendarSelectionMode.DayWeekMonth);
1005 private bool IsTheSameYearMonth(DateTime date1, DateTime date2) {
1007 Debug.Assert(threadCalendarInitialized);
1009 return (threadCalendar.GetEra(date1) == threadCalendar.GetEra(date2) &&
1010 threadCalendar.GetYear(date1) == threadCalendar.GetYear(date2) &&
1011 threadCalendar.GetMonth(date1) == threadCalendar.GetMonth(date2));
1014 private bool IsMinSupportedYearMonth(DateTime date) {
1016 Debug.Assert(threadCalendarInitialized);
1018 return IsTheSameYearMonth(minSupportedDate, date);
1021 private bool IsMaxSupportedYearMonth(DateTime date) {
1023 Debug.Assert(threadCalendarInitialized);
1025 return IsTheSameYearMonth(maxSupportedDate, date);
1030 /// <para>Loads a saved state of the <see cref='System.Web.UI.WebControls.Calendar'/>. </para>
1032 protected override void LoadViewState(object savedState) {
1033 if (savedState != null) {
1034 object[] myState = (object[])savedState;
1036 if (myState[0] != null)
1037 base.LoadViewState(myState[0]);
1038 if (myState[1] != null)
1039 ((IStateManager)TitleStyle).LoadViewState(myState[1]);
1040 if (myState[2] != null)
1041 ((IStateManager)NextPrevStyle).LoadViewState(myState[2]);
1042 if (myState[3] != null)
1043 ((IStateManager)DayStyle).LoadViewState(myState[3]);
1044 if (myState[4] != null)
1045 ((IStateManager)DayHeaderStyle).LoadViewState(myState[4]);
1046 if (myState[5] != null)
1047 ((IStateManager)TodayDayStyle).LoadViewState(myState[5]);
1048 if (myState[6] != null)
1049 ((IStateManager)WeekendDayStyle).LoadViewState(myState[6]);
1050 if (myState[7] != null)
1051 ((IStateManager)OtherMonthDayStyle).LoadViewState(myState[7]);
1052 if (myState[8] != null)
1053 ((IStateManager)SelectedDayStyle).LoadViewState(myState[8]);
1054 if (myState[9] != null)
1055 ((IStateManager)SelectorStyle).LoadViewState(myState[9]);
1057 ArrayList selDates = (ArrayList)ViewState["SD"];
1058 if (selDates != null) {
1059 dateList = selDates;
1060 selectedDates = null; // reset wrapper collection
1069 /// <para>Marks the starting point to begin tracking and saving changes to the
1070 /// control as part of the control viewstate.</para>
1072 protected override void TrackViewState() {
1073 base.TrackViewState();
1075 if (titleStyle != null)
1076 ((IStateManager)titleStyle).TrackViewState();
1077 if (nextPrevStyle != null)
1078 ((IStateManager)nextPrevStyle).TrackViewState();
1079 if (dayStyle != null)
1080 ((IStateManager)dayStyle).TrackViewState();
1081 if (dayHeaderStyle != null)
1082 ((IStateManager)dayHeaderStyle).TrackViewState();
1083 if (todayDayStyle != null)
1084 ((IStateManager)todayDayStyle).TrackViewState();
1085 if (weekendDayStyle != null)
1086 ((IStateManager)weekendDayStyle).TrackViewState();
1087 if (otherMonthDayStyle != null)
1088 ((IStateManager)otherMonthDayStyle).TrackViewState();
1089 if (selectedDayStyle != null)
1090 ((IStateManager)selectedDayStyle).TrackViewState();
1091 if (selectorStyle != null)
1092 ((IStateManager)selectorStyle).TrackViewState();
1098 private int NumericFirstDayOfWeek() {
1099 // Used globalized value by default
1100 return(FirstDayOfWeek == FirstDayOfWeek.Default)
1101 ? (int) DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek
1102 : (int) FirstDayOfWeek;
1107 /// <para>Raises the <see langword='DayRender '/>event for a <see cref='System.Web.UI.WebControls.Calendar'/>.</para>
1109 protected virtual void OnDayRender(TableCell cell, CalendarDay day) {
1110 DayRenderEventHandler handler = (DayRenderEventHandler)Events[EventDayRender];
1111 if (handler != null) {
1112 int absoluteDay = day.Date.Subtract(baseDate).Days;
1114 // VSWhidbey 215383: We return null for selectUrl if a control is not in
1115 // the page control tree.
1116 string selectUrl = null;
1119 string eventArgument = absoluteDay.ToString(CultureInfo.InvariantCulture);
1120 selectUrl = Page.ClientScript.GetPostBackClientHyperlink(this, eventArgument, true);
1122 handler(this, new DayRenderEventArgs(cell, day, selectUrl));
1127 /// <para>Raises the <see langword='SelectionChanged '/>event for a <see cref='System.Web.UI.WebControls.Calendar'/>.</para>
1129 protected virtual void OnSelectionChanged() {
1130 EventHandler handler = (EventHandler)Events[EventSelectionChanged];
1131 if (handler != null) {
1132 handler(this, EventArgs.Empty);
1138 /// <para>Raises the <see langword='VisibleMonthChanged '/>event for a <see cref='System.Web.UI.WebControls.Calendar'/>.</para>
1140 protected virtual void OnVisibleMonthChanged(DateTime newDate, DateTime previousDate) {
1141 MonthChangedEventHandler handler = (MonthChangedEventHandler)Events[EventVisibleMonthChanged];
1142 if (handler != null) {
1143 handler(this, new MonthChangedEventArgs(newDate, previousDate));
1150 /// <para>Raises events on post back for the <see cref='System.Web.UI.WebControls.Calendar'/> control.</para>
1152 protected virtual void RaisePostBackEvent(string eventArgument) {
1154 ValidateEvent(UniqueID, eventArgument);
1156 if (AdapterInternal != null) {
1157 IPostBackEventHandler pbeh = AdapterInternal as IPostBackEventHandler;
1159 pbeh.RaisePostBackEvent(eventArgument);
1163 if (String.Compare(eventArgument, 0, NAVIGATE_MONTH_COMMAND, 0, NAVIGATE_MONTH_COMMAND.Length, StringComparison.Ordinal) == 0) {
1164 // Month navigation. The command starts with a "V" and the remainder is day difference from the
1166 DateTime oldDate = VisibleDate;
1167 if (oldDate.Equals(DateTime.MinValue)) {
1168 oldDate = TodaysDate;
1170 int newDateDiff = Int32.Parse(eventArgument.Substring(NAVIGATE_MONTH_COMMAND.Length), CultureInfo.InvariantCulture);
1171 VisibleDate = baseDate.AddDays(newDateDiff);
1172 if (VisibleDate == DateTime.MinValue) {
1173 // MinValue would make the calendar shows today's month instead because it
1174 // is the default value of VisibleDate property, so we add a day to keep
1175 // showing the first supported month.
1176 // We assume the first supported month has more than one day.
1177 VisibleDate = DateTimeFormatInfo.CurrentInfo.Calendar.AddDays(VisibleDate, 1);
1179 OnVisibleMonthChanged(VisibleDate, oldDate);
1181 else if (String.Compare(eventArgument, 0, SELECT_RANGE_COMMAND, 0, SELECT_RANGE_COMMAND.Length, StringComparison.Ordinal) == 0) {
1182 // Range selection. The command starts with an "R". The remainder is an integer. When divided by 100
1183 // the result is the day difference from the base date of the first day, and the remainder is the
1184 // number of days to select.
1185 int rangeValue = Int32.Parse(eventArgument.Substring(SELECT_RANGE_COMMAND.Length), CultureInfo.InvariantCulture);
1186 int dayDiff = rangeValue / 100;
1187 int dayRange = rangeValue % 100;
1189 dayRange = 100 + dayRange;
1192 DateTime dt = baseDate.AddDays(dayDiff);
1193 SelectRange(dt, dt.AddDays(dayRange - 1));
1196 // Single day selection. This is just a number which is the day difference from the base date.
1197 int dayDiff = Int32.Parse(eventArgument, CultureInfo.InvariantCulture);
1198 DateTime dt = baseDate.AddDays(dayDiff);
1199 SelectRange(dt, dt);
1205 void IPostBackEventHandler.RaisePostBackEvent(string eventArgument) {
1206 RaisePostBackEvent(eventArgument);
1211 protected internal override void OnPreRender(EventArgs e) {
1212 base.OnPreRender(e);
1214 Page.RegisterPostBackScript();
1221 /// <para>Displays the <see cref='System.Web.UI.WebControls.Calendar'/> control on the client.</para>
1223 protected internal override void Render(HtmlTextWriter writer) {
1224 threadCalendar = DateTimeFormatInfo.CurrentInfo.Calendar;
1225 minSupportedDate = threadCalendar.MinSupportedDateTime;
1226 maxSupportedDate = threadCalendar.MaxSupportedDateTime;
1228 threadCalendarInitialized = true;
1230 DateTime visibleDate = EffectiveVisibleDate();
1231 DateTime firstDay = FirstCalendarDay(visibleDate);
1232 CalendarSelectionMode selectionMode = SelectionMode;
1234 // Make sure we are in a form tag with runat=server.
1236 Page.VerifyRenderingInServerForm(this);
1239 // We only want to display the link if we have a page, or if we are on the design surface
1240 // If we can stops links being active on the Autoformat dialog, then we can remove this these checks.
1243 if (page == null || DesignMode) {
1244 buttonsActive = false;
1247 buttonsActive = IsEnabled;
1250 defaultForeColor = ForeColor;
1251 if (defaultForeColor == Color.Empty) {
1252 defaultForeColor = DefaultForeColor;
1254 defaultButtonColorText = ColorTranslator.ToHtml(defaultForeColor);
1256 Table table = new Table();
1259 table.ID = ClientID;
1261 table.CopyBaseAttributes(this);
1262 if (ControlStyleCreated) {
1263 table.ApplyStyle(ControlStyle);
1265 table.Width = Width;
1266 table.Height = Height;
1267 table.CellPadding = CellPadding;
1268 table.CellSpacing = CellSpacing;
1271 if ((ControlStyleCreated == false) ||
1272 (ControlStyle.IsSet(System.Web.UI.WebControls.Style.PROP_BORDERWIDTH) == false) ||
1273 BorderWidth.Equals(Unit.Empty)) {
1274 table.BorderWidth = Unit.Pixel(1);
1277 if (ShowGridLines) {
1278 table.GridLines = GridLines.Both;
1281 table.GridLines = GridLines.None;
1284 bool useAccessibleHeader = UseAccessibleHeader;
1285 if (useAccessibleHeader) {
1286 if (table.Attributes["title"] == null) {
1287 table.Attributes["title"] = SR.GetString(SR.Calendar_TitleText);
1291 string caption = Caption;
1292 if (caption.Length > 0) {
1293 table.Caption = caption;
1294 table.CaptionAlign = CaptionAlign;
1297 table.RenderBeginTag(writer);
1300 RenderTitle(writer, visibleDate, selectionMode, buttonsActive, useAccessibleHeader);
1303 if (ShowDayHeader) {
1304 RenderDayHeader(writer, visibleDate, selectionMode, buttonsActive, useAccessibleHeader);
1307 RenderDays(writer, firstDay, visibleDate, selectionMode, buttonsActive, useAccessibleHeader);
1309 table.RenderEndTag(writer);
1312 private void RenderCalendarCell(HtmlTextWriter writer, TableItemStyle style, string text, string title, bool hasButton, string eventArgument) {
1313 style.AddAttributesToRender(writer, this);
1314 writer.RenderBeginTag(HtmlTextWriterTag.Td);
1318 // render the button
1319 Color foreColor = style.ForeColor;
1320 writer.Write("<a href=\"");
1321 writer.Write(Page.ClientScript.GetPostBackClientHyperlink(this, eventArgument, true));
1323 // ForeColor needs to go on the actual link. This breaks the uplevel/downlevel rules a little bit,
1324 // but it is worth doing so the day links do not change color when they go in the history on
1325 // downlevel browsers. Otherwise, people get it confused with the selection mechanism.
1326 writer.Write("\" style=\"color:");
1327 writer.Write(foreColor.IsEmpty ? defaultButtonColorText : ColorTranslator.ToHtml(foreColor));
1329 if (!String.IsNullOrEmpty(title)) {
1330 writer.Write("\" title=\"");
1331 writer.Write(title);
1334 writer.Write("\">");
1336 writer.Write("</a>");
1342 writer.RenderEndTag();
1345 private void RenderCalendarHeaderCell(HtmlTextWriter writer, TableItemStyle style, string text, string abbrText) {
1346 style.AddAttributesToRender(writer, this);
1347 writer.AddAttribute("abbr", abbrText);
1348 writer.AddAttribute("scope", "col");
1349 writer.RenderBeginTag(HtmlTextWriterTag.Th);
1351 writer.RenderEndTag();
1357 private void RenderDayHeader(HtmlTextWriter writer, DateTime visibleDate, CalendarSelectionMode selectionMode, bool buttonsActive, bool useAccessibleHeader) {
1359 writer.Write(ROWBEGINTAG);
1361 DateTimeFormatInfo dtf = DateTimeFormatInfo.CurrentInfo;
1363 if (HasWeekSelectors(selectionMode)) {
1364 TableItemStyle monthSelectorStyle = new TableItemStyle();
1365 monthSelectorStyle.HorizontalAlign = HorizontalAlign.Center;
1366 // add the month selector button if required;
1367 if (selectionMode == CalendarSelectionMode.DayWeekMonth) {
1369 // Range selection. The command starts with an "R". The remainder is an integer. When divided by 100
1370 // the result is the day difference from the base date of the first day, and the remainder is the
1371 // number of days to select.
1372 int startOffset = visibleDate.Subtract(baseDate).Days;
1373 int monthLength = threadCalendar.GetDaysInMonth(threadCalendar.GetYear(visibleDate), threadCalendar.GetMonth(visibleDate), threadCalendar.GetEra(visibleDate));
1374 if (IsMinSupportedYearMonth(visibleDate)) {
1375 // The first supported month might not start with day 1
1376 // (e.g. Sept 8 is the first supported date of JapaneseCalendar)
1377 monthLength = monthLength - threadCalendar.GetDayOfMonth(visibleDate) + 1;
1379 else if (IsMaxSupportedYearMonth(visibleDate)) {
1380 // The last supported month might not have all days supported in that calendar month
1381 // (e.g. April 3 is the last supported date of HijriCalendar)
1382 monthLength = threadCalendar.GetDayOfMonth(maxSupportedDate);
1385 string monthSelectKey = SELECT_RANGE_COMMAND + ((startOffset * 100) + monthLength).ToString(CultureInfo.InvariantCulture);
1386 monthSelectorStyle.CopyFrom(SelectorStyle);
1388 string selectMonthTitle = null;
1389 if (useAccessibleHeader) {
1390 selectMonthTitle = SR.GetString(SR.Calendar_SelectMonthTitle);
1392 RenderCalendarCell(writer, monthSelectorStyle, SelectMonthText, selectMonthTitle, buttonsActive, monthSelectKey);
1395 // otherwise make it look like the header row
1396 monthSelectorStyle.CopyFrom(DayHeaderStyle);
1397 RenderCalendarCell(writer, monthSelectorStyle, string.Empty, null, false, null);
1401 TableItemStyle dayNameStyle = new TableItemStyle();
1402 dayNameStyle.HorizontalAlign = HorizontalAlign.Center;
1403 dayNameStyle.CopyFrom(DayHeaderStyle);
1404 DayNameFormat dayNameFormat = DayNameFormat;
1406 int numericFirstDay = NumericFirstDayOfWeek();
1407 for (int i = numericFirstDay; i < numericFirstDay + 7; i++) {
1409 int dayOfWeek = i % 7;
1410 switch (dayNameFormat) {
1411 case DayNameFormat.FirstLetter:
1412 dayName = dtf.GetDayName((DayOfWeek)dayOfWeek).Substring(0, 1);
1414 case DayNameFormat.FirstTwoLetters:
1415 dayName = dtf.GetDayName((DayOfWeek)dayOfWeek).Substring(0, 2);
1417 case DayNameFormat.Full:
1418 dayName = dtf.GetDayName((DayOfWeek)dayOfWeek);
1420 case DayNameFormat.Short:
1421 dayName = dtf.GetAbbreviatedDayName((DayOfWeek)dayOfWeek);
1423 case DayNameFormat.Shortest:
1424 dayName = dtf.GetShortestDayName((DayOfWeek)dayOfWeek);
1427 Debug.Assert(false, "Unknown DayNameFormat value!");
1428 goto case DayNameFormat.Short;
1431 if (useAccessibleHeader) {
1432 string fullDayName = dtf.GetDayName((DayOfWeek)dayOfWeek);
1433 RenderCalendarHeaderCell(writer, dayNameStyle, dayName, fullDayName);
1436 RenderCalendarCell(writer, dayNameStyle, dayName, null, false, null);
1439 writer.Write(ROWENDTAG);
1445 private void RenderDays(HtmlTextWriter writer, DateTime firstDay, DateTime visibleDate, CalendarSelectionMode selectionMode, bool buttonsActive, bool useAccessibleHeader) {
1446 // Now add the rows for the actual days
1448 DateTime d = firstDay;
1449 TableItemStyle weekSelectorStyle = null;
1451 bool hasWeekSelectors = HasWeekSelectors(selectionMode);
1452 if (hasWeekSelectors) {
1453 weekSelectorStyle = new TableItemStyle();
1454 weekSelectorStyle.Width = Unit.Percentage(12);
1455 weekSelectorStyle.HorizontalAlign = HorizontalAlign.Center;
1456 weekSelectorStyle.CopyFrom(SelectorStyle);
1457 defaultWidth = Unit.Percentage(12);
1460 defaultWidth = Unit.Percentage(14);
1463 // This determines whether we need to call DateTime.ToString for each day. The only culture/calendar
1464 // that requires this for now is the HebrewCalendar.
1465 bool usesStandardDayDigits = !(threadCalendar is HebrewCalendar);
1467 // This determines whether we can write out cells directly, or whether we have to create whole
1468 // TableCell objects for each day.
1469 bool hasRenderEvent = (this.GetType() != typeof(Calendar)
1470 || Events[EventDayRender] != null);
1472 TableItemStyle [] cellStyles = new TableItemStyle[16];
1473 int definedStyleMask = GetDefinedStyleMask();
1474 DateTime todaysDate = TodaysDate;
1475 string selectWeekText = SelectWeekText;
1476 bool daysSelectable = buttonsActive && (selectionMode != CalendarSelectionMode.None);
1477 int visibleDateMonth = threadCalendar.GetMonth(visibleDate);
1478 int absoluteDay = firstDay.Subtract(baseDate).Days;
1480 // VSWhidbey 480155: flag to indicate if forecolor needs to be set
1481 // explicitly in design mode to mimic runtime rendering with the
1482 // limitation of not supporting CSS class color setting.
1483 bool inDesignSelectionMode = (DesignMode && SelectionMode != CalendarSelectionMode.None);
1485 //------------------------------------------------------------------
1486 // VSWhidbey 366243: The following variables are for boundary cases
1487 // such as the current visible month is the first or the last
1488 // supported month. They are used in the 'for' loops below.
1490 // For the first supported month, calculate how many days to
1491 // skip at the beginning of the first month. E.g. JapaneseCalendar
1492 // starts at Sept 8.
1493 int numOfFirstDaysToSkip = 0;
1494 if (IsMinSupportedYearMonth(visibleDate)) {
1495 numOfFirstDaysToSkip = (int)threadCalendar.GetDayOfWeek(firstDay) - NumericFirstDayOfWeek();
1496 // If negative, it simply means the the index of the starting
1497 // day name is greater than the day name of the first supported
1498 // date. We add back 7 to get the number of days to skip.
1499 if (numOfFirstDaysToSkip < 0) {
1500 numOfFirstDaysToSkip += 7;
1503 Debug.Assert(numOfFirstDaysToSkip < 7);
1505 // For the last or second last supported month, initialize variables
1506 // to identify the last supported date of the current calendar.
1507 // e.g. The last supported date of HijriCalendar is April 3. When
1508 // the second last monthh is shown, it can be the case that not all
1509 // cells will be filled up.
1510 bool passedLastSupportedDate = false;
1511 DateTime secondLastMonth = threadCalendar.AddMonths(maxSupportedDate, -1);
1512 bool lastOrSecondLastMonth = (IsMaxSupportedYearMonth(visibleDate) ||
1513 IsTheSameYearMonth(secondLastMonth, visibleDate));
1514 //------------------------------------------------------------------
1516 for (int iRow = 0; iRow < 6; iRow++) {
1517 if (passedLastSupportedDate) {
1521 writer.Write(ROWBEGINTAG);
1523 // add week selector column and button if required
1524 if (hasWeekSelectors) {
1525 // Range selection. The command starts with an "R". The remainder is an integer. When divided by 100
1526 // the result is the day difference from the base date of the first day, and the remainder is the
1527 // number of days to select.
1528 int dayDiffParameter = (absoluteDay * 100) + 7;
1530 // Adjust the dayDiff for the first or the last supported month
1531 if (numOfFirstDaysToSkip > 0) {
1532 dayDiffParameter -= numOfFirstDaysToSkip;
1534 else if (lastOrSecondLastMonth) {
1535 int daysFromLastDate = maxSupportedDate.Subtract(d).Days;
1536 if (daysFromLastDate < 6) {
1537 dayDiffParameter -= (6 - daysFromLastDate);
1540 string weekSelectKey = SELECT_RANGE_COMMAND + dayDiffParameter.ToString(CultureInfo.InvariantCulture);
1542 string selectWeekTitle = null;
1543 if (useAccessibleHeader) {
1544 int weekOfMonth = iRow + 1;
1545 selectWeekTitle = SR.GetString(SR.Calendar_SelectWeekTitle, weekOfMonth.ToString(CultureInfo.InvariantCulture));
1547 RenderCalendarCell(writer, weekSelectorStyle, selectWeekText, selectWeekTitle, buttonsActive, weekSelectKey);
1550 for (int iDay = 0; iDay < 7; iDay++) {
1552 // Render empty cells for special cases to handle the first
1553 // or last supported month.
1554 if (numOfFirstDaysToSkip > 0) {
1555 iDay += numOfFirstDaysToSkip;
1556 for ( ; numOfFirstDaysToSkip > 0; numOfFirstDaysToSkip--) {
1557 writer.RenderBeginTag(HtmlTextWriterTag.Td);
1558 writer.RenderEndTag();
1561 else if (passedLastSupportedDate) {
1562 for ( ; iDay < 7; iDay++) {
1563 writer.RenderBeginTag(HtmlTextWriterTag.Td);
1564 writer.RenderEndTag();
1569 int dayOfWeek = (int)threadCalendar.GetDayOfWeek(d);
1570 int dayOfMonth = threadCalendar.GetDayOfMonth(d);
1571 string dayNumberText;
1572 if ((dayOfMonth <= cachedNumberMax) && usesStandardDayDigits) {
1573 dayNumberText = cachedNumbers[dayOfMonth];
1576 dayNumberText = d.ToString("dd", CultureInfo.CurrentCulture);
1579 CalendarDay day = new CalendarDay(d,
1580 (dayOfWeek == 0 || dayOfWeek == 6), // IsWeekend
1581 d.Equals(todaysDate), // IsToday
1582 (selectedDates != null) && selectedDates.Contains(d), // IsSelected
1583 threadCalendar.GetMonth(d) != visibleDateMonth, // IsOtherMonth
1584 dayNumberText // Number Text
1587 int styleMask = STYLEMASK_DAY;
1589 styleMask |= STYLEMASK_SELECTED;
1590 if (day.IsOtherMonth)
1591 styleMask |= STYLEMASK_OTHERMONTH;
1593 styleMask |= STYLEMASK_TODAY;
1595 styleMask |= STYLEMASK_WEEKEND;
1596 int dayStyleMask = definedStyleMask & styleMask;
1597 // determine the unique portion of the mask for the current calendar,
1598 // which will strip out the day style bit
1599 int dayStyleID = dayStyleMask & STYLEMASK_UNIQUE;
1601 TableItemStyle cellStyle = cellStyles[dayStyleID];
1602 if (cellStyle == null) {
1603 cellStyle = new TableItemStyle();
1604 SetDayStyles(cellStyle, dayStyleMask, defaultWidth);
1605 cellStyles[dayStyleID] = cellStyle;
1609 string dayTitle = null;
1610 if (useAccessibleHeader) {
1611 dayTitle = d.ToString("m", CultureInfo.CurrentCulture);
1614 if (hasRenderEvent) {
1616 TableCell cdc = new TableCell();
1617 cdc.ApplyStyle(cellStyle);
1619 LiteralControl dayContent = new LiteralControl(dayNumberText);
1620 cdc.Controls.Add(dayContent);
1622 day.IsSelectable = daysSelectable;
1624 OnDayRender(cdc, day);
1626 // refresh the day content
1627 dayContent.Text = GetCalendarButtonText(absoluteDay.ToString(CultureInfo.InvariantCulture),
1630 buttonsActive && day.IsSelectable,
1632 cdc.RenderControl(writer);
1636 // VSWhidbey 480155: In design mode we render days as
1637 // texts instead of links so CSS class color setting is
1638 // supported. But this differs in runtime rendering
1639 // where CSS class color setting is not supported. To
1640 // correctly mimic the forecolor of runtime rendering in
1641 // design time, the default color, which is used in
1642 // runtime rendering, is explicitly set in this case.
1643 if (inDesignSelectionMode && cellStyle.ForeColor.IsEmpty) {
1644 cellStyle.ForeColor = defaultForeColor;
1647 RenderCalendarCell(writer, cellStyle, dayNumberText, dayTitle, daysSelectable, absoluteDay.ToString(CultureInfo.InvariantCulture));
1650 Debug.Assert(!passedLastSupportedDate);
1651 if (lastOrSecondLastMonth && d.Month == maxSupportedDate.Month && d.Day == maxSupportedDate.Day) {
1652 passedLastSupportedDate = true;
1655 d = threadCalendar.AddDays(d, 1);
1659 writer.Write(ROWENDTAG);
1666 private void RenderTitle(HtmlTextWriter writer, DateTime visibleDate, CalendarSelectionMode selectionMode, bool buttonsActive, bool useAccessibleHeader) {
1667 writer.Write(ROWBEGINTAG);
1669 TableCell titleCell = new TableCell();
1670 Table titleTable = new Table();
1672 // default title table/cell styles
1673 titleCell.ColumnSpan = HasWeekSelectors(selectionMode) ? 8 : 7;
1674 titleCell.BackColor = Color.Silver;
1675 titleTable.GridLines = GridLines.None;
1676 titleTable.Width = Unit.Percentage(100);
1677 titleTable.CellSpacing = 0;
1679 TableItemStyle titleStyle = TitleStyle;
1680 ApplyTitleStyle(titleCell, titleTable, titleStyle);
1682 titleCell.RenderBeginTag(writer);
1683 titleTable.RenderBeginTag(writer);
1684 writer.Write(ROWBEGINTAG);
1686 NextPrevFormat nextPrevFormat = NextPrevFormat;
1688 TableItemStyle nextPrevStyle = new TableItemStyle();
1689 nextPrevStyle.Width = Unit.Percentage(15);
1690 nextPrevStyle.CopyFrom(NextPrevStyle);
1691 if (ShowNextPrevMonth) {
1692 if (IsMinSupportedYearMonth(visibleDate)) {
1693 writer.RenderBeginTag(HtmlTextWriterTag.Td);
1694 writer.RenderEndTag();
1697 string prevMonthText;
1698 if (nextPrevFormat == NextPrevFormat.ShortMonth || nextPrevFormat == NextPrevFormat.FullMonth) {
1699 int monthNo = threadCalendar.GetMonth(threadCalendar.AddMonths(visibleDate, - 1));
1700 prevMonthText = GetMonthName(monthNo, (nextPrevFormat == NextPrevFormat.FullMonth));
1703 prevMonthText = PrevMonthText;
1705 // Month navigation. The command starts with a "V" and the remainder is day difference from the
1707 DateTime prevMonthDate;
1709 // VSWhidbey 366243: Some calendar's min supported date is
1710 // not the first day of the month (e.g. JapaneseCalendar.
1711 // So if we are setting the second supported month, the prev
1712 // month link should always point to the first supported
1713 // date instead of the first day of the previous month.
1714 DateTime secondSupportedMonth = threadCalendar.AddMonths(minSupportedDate, 1);
1715 if (IsTheSameYearMonth(secondSupportedMonth, visibleDate)) {
1716 prevMonthDate = minSupportedDate;
1719 prevMonthDate = threadCalendar.AddMonths(visibleDate, -1);
1722 string prevMonthKey = NAVIGATE_MONTH_COMMAND + (prevMonthDate.Subtract(baseDate)).Days.ToString(CultureInfo.InvariantCulture);
1724 string previousMonthTitle = null;
1725 if (useAccessibleHeader) {
1726 previousMonthTitle = SR.GetString(SR.Calendar_PreviousMonthTitle);
1728 RenderCalendarCell(writer, nextPrevStyle, prevMonthText, previousMonthTitle, buttonsActive, prevMonthKey);
1733 TableItemStyle cellMainStyle = new TableItemStyle();
1735 if (titleStyle.HorizontalAlign != HorizontalAlign.NotSet) {
1736 cellMainStyle.HorizontalAlign = titleStyle.HorizontalAlign;
1739 cellMainStyle.HorizontalAlign = HorizontalAlign.Center;
1741 cellMainStyle.Wrap = titleStyle.Wrap;
1742 cellMainStyle.Width = Unit.Percentage(70);
1746 switch (TitleFormat) {
1747 case TitleFormat.Month:
1748 titleText = visibleDate.ToString("MMMM", CultureInfo.CurrentCulture);
1750 case TitleFormat.MonthYear:
1751 string titlePattern = DateTimeFormatInfo.CurrentInfo.YearMonthPattern;
1752 // Some cultures have a comma in their YearMonthPattern, which does not look
1753 // right in a calendar. Use a fixed pattern for those.
1754 if (titlePattern.IndexOf(',') >= 0) {
1755 titlePattern = "MMMM yyyy";
1757 titleText = visibleDate.ToString(titlePattern, CultureInfo.CurrentCulture);
1760 Debug.Assert(false, "Unknown TitleFormat value!");
1761 goto case TitleFormat.MonthYear;
1763 RenderCalendarCell(writer, cellMainStyle, titleText, null, false, null);
1765 if (ShowNextPrevMonth) {
1766 if (IsMaxSupportedYearMonth(visibleDate)) {
1767 writer.RenderBeginTag(HtmlTextWriterTag.Td);
1768 writer.RenderEndTag();
1771 // Style for this one is identical bar
1772 nextPrevStyle.HorizontalAlign = HorizontalAlign.Right;
1773 string nextMonthText;
1774 if (nextPrevFormat == NextPrevFormat.ShortMonth || nextPrevFormat == NextPrevFormat.FullMonth) {
1775 int monthNo = threadCalendar.GetMonth(threadCalendar.AddMonths(visibleDate, 1));
1776 nextMonthText = GetMonthName(monthNo, (nextPrevFormat == NextPrevFormat.FullMonth));
1779 nextMonthText = NextMonthText;
1781 // Month navigation. The command starts with a "V" and the remainder is day difference from the
1783 DateTime nextMonthDate = threadCalendar.AddMonths(visibleDate, 1);
1784 string nextMonthKey = NAVIGATE_MONTH_COMMAND + (nextMonthDate.Subtract(baseDate)).Days.ToString(CultureInfo.InvariantCulture);
1786 string nextMonthTitle = null;
1787 if (useAccessibleHeader) {
1788 nextMonthTitle = SR.GetString(SR.Calendar_NextMonthTitle);
1790 RenderCalendarCell(writer, nextPrevStyle, nextMonthText, nextMonthTitle, buttonsActive, nextMonthKey);
1793 writer.Write(ROWENDTAG);
1794 titleTable.RenderEndTag(writer);
1795 titleCell.RenderEndTag(writer);
1796 writer.Write(ROWENDTAG);
1803 /// <para>Stores the state of the System.Web.UI.WebControls.Calender.</para>
1805 protected override object SaveViewState() {
1806 if (SelectedDates.Count > 0)
1807 ViewState["SD"] = dateList;
1809 object[] myState = new object[10];
1811 myState[0] = base.SaveViewState();
1812 myState[1] = (titleStyle != null) ? ((IStateManager)titleStyle).SaveViewState() : null;
1813 myState[2] = (nextPrevStyle != null) ? ((IStateManager)nextPrevStyle).SaveViewState() : null;
1814 myState[3] = (dayStyle != null) ? ((IStateManager)dayStyle).SaveViewState() : null;
1815 myState[4] = (dayHeaderStyle != null) ? ((IStateManager)dayHeaderStyle).SaveViewState() : null;
1816 myState[5] = (todayDayStyle != null) ? ((IStateManager)todayDayStyle).SaveViewState() : null;
1817 myState[6] = (weekendDayStyle != null) ? ((IStateManager)weekendDayStyle).SaveViewState() : null;
1818 myState[7] = (otherMonthDayStyle != null) ? ((IStateManager)otherMonthDayStyle).SaveViewState() : null;
1819 myState[8] = (selectedDayStyle != null) ? ((IStateManager)selectedDayStyle).SaveViewState() : null;
1820 myState[9] = (selectorStyle != null) ? ((IStateManager)selectorStyle).SaveViewState() : null;
1822 for (int i = 0; i<myState.Length; i++) {
1823 if (myState[i] != null)
1830 private void SelectRange(DateTime dateFrom, DateTime dateTo) {
1832 Debug.Assert(dateFrom <= dateTo, "Bad Date Range");
1834 // see if this range differs in any way from the current range
1835 // these checks will determine this because the colleciton is sorted
1836 TimeSpan ts = dateTo - dateFrom;
1837 if (SelectedDates.Count != ts.Days + 1
1838 || SelectedDates[0] != dateFrom
1839 || SelectedDates[SelectedDates.Count - 1] != dateTo) {
1840 SelectedDates.SelectRange(dateFrom, dateTo);
1841 OnSelectionChanged();
1848 private void SetDayStyles(TableItemStyle style, int styleMask, Unit defaultWidth) {
1850 // default day styles
1851 style.Width = defaultWidth;
1852 style.HorizontalAlign = HorizontalAlign.Center;
1854 if ((styleMask & STYLEMASK_DAY) != 0) {
1855 style.CopyFrom(DayStyle);
1857 if ((styleMask & STYLEMASK_WEEKEND) != 0) {
1858 style.CopyFrom(WeekendDayStyle);
1860 if ((styleMask & STYLEMASK_OTHERMONTH) != 0) {
1861 style.CopyFrom(OtherMonthDayStyle);
1863 if ((styleMask & STYLEMASK_TODAY) != 0) {
1864 style.CopyFrom(TodayDayStyle);
1867 if ((styleMask & STYLEMASK_SELECTED) != 0) {
1868 // default selected day style
1869 style.ForeColor = Color.White;
1870 style.BackColor = Color.Silver;
1872 style.CopyFrom(SelectedDayStyle);