1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004-2005 Novell, Inc.
23 // John BouAntoun jba-mono@optusnet.com.au
24 // Rolf Bjarne Kvinge rolfkvinge@ya.com
27 // - wire in all events from monthcalendar
32 using System
.Collections
;
33 using System
.ComponentModel
;
34 using System
.Runtime
.InteropServices
;
35 using System
.Windows
.Forms
;
37 namespace System
.Windows
.Forms
{
39 [ClassInterface (ClassInterfaceType
.AutoDispatch
)]
40 [DefaultBindingProperty ("Value")]
43 [DefaultEvent("ValueChanged")]
44 [DefaultProperty("Value")]
45 [Designer("System.Windows.Forms.Design.DateTimePickerDesigner, " + Consts
.AssemblySystem_Design
, "System.ComponentModel.Design.IDesigner")]
46 public class DateTimePicker
: Control
{
48 #region Public variables
50 // this class has to have the specified hour, minute and second, as it says in msdn
52 [EditorBrowsable (EditorBrowsableState
.Never
)]
55 public static readonly DateTime MaxDateTime
= new DateTime (9998, 12, 31, 0, 0, 0);
57 [EditorBrowsable (EditorBrowsableState
.Never
)]
60 public static readonly DateTime MinDateTime
= new DateTime (1753, 1, 1);
62 internal const int check_box_size
= 13;
63 internal const int check_box_space
= 4;
65 #endregion // Public variables
67 #region Local variables
69 protected static readonly Color DefaultMonthBackColor
= ThemeEngine
.Current
.ColorWindow
;
70 protected static readonly Color DefaultTitleBackColor
= ThemeEngine
.Current
.ColorActiveCaption
;
71 protected static readonly Color DefaultTitleForeColor
= ThemeEngine
.Current
.ColorActiveCaptionText
;
72 protected static readonly Color DefaultTrailingForeColor
= SystemColors
.GrayText
;
74 internal MonthCalendar month_calendar
;
77 LeftRightAlignment drop_down_align
;
78 DateTimePickerFormat format
;
85 bool right_to_left_layout
;
87 // variables used for drawing and such
88 internal const int up_down_width
= check_box_size
;
89 internal bool is_drop_down_visible
;
90 internal bool is_up_pressed
;
91 internal bool is_down_pressed
;
92 internal Timer updown_timer
;
93 internal const int initial_timer_delay
= 500;
94 internal const int subsequent_timer_delay
= 100;
95 internal bool is_checkbox_selected
;
97 // variables for determining how to format the string
98 internal PartData
[] part_data
;
100 #endregion // Local variables
102 #region DateTimePickerAccessibleObject Subclass
104 public class DateTimePickerAccessibleObject
: ControlAccessibleObject
{
105 #region DateTimePickerAccessibleObject Local Variables
106 private new DateTimePicker owner
;
107 #endregion // DateTimePickerAccessibleObject Local Variables
109 #region DateTimePickerAccessibleObject Constructors
110 public DateTimePickerAccessibleObject(DateTimePicker owner
) : base(owner
) {
113 #endregion // DateTimePickerAccessibleObject Constructors
115 #region DateTimePickerAccessibleObject Properties
117 public override string KeyboardShortcut
{
119 return base.KeyboardShortcut
;
123 public override AccessibleRole Role
{
129 public override AccessibleStates State
{
131 AccessibleStates retval
;
133 retval
= AccessibleStates
.Default
;
136 retval
|= AccessibleStates
.Checked
;
143 public override string Value
{
148 #endregion // DateTimePickerAccessibleObject Properties
150 #endregion // DateTimePickerAccessibleObject Sub-class
152 #region public constructors
154 // only public constructor
155 public DateTimePicker () {
157 // initialise the month calendar
158 month_calendar
= new MonthCalendar (this);
159 month_calendar
.CalendarDimensions
= new Size (1, 1);
160 month_calendar
.MaxSelectionCount
= 1;
161 month_calendar
.ForeColor
= Control
.DefaultForeColor
;
162 month_calendar
.BackColor
= DefaultMonthBackColor
;
163 month_calendar
.TitleBackColor
= DefaultTitleBackColor
;
164 month_calendar
.TitleForeColor
= DefaultTitleForeColor
;
165 month_calendar
.TrailingForeColor
= DefaultTrailingForeColor
;
166 month_calendar
.Visible
= false;
167 // initialize the timer
168 updown_timer
= new Timer();
169 updown_timer
.Interval
= initial_timer_delay
;
172 // initialise other variables
174 custom_format
= null;
175 drop_down_align
= LeftRightAlignment
.Left
;
176 format
= DateTimePickerFormat
.Long
;
177 max_date
= MaxDateTime
;
178 min_date
= MinDateTime
;
179 show_check_box
= false;
180 show_up_down
= false;
181 date_value
= DateTime
.Now
;
183 is_drop_down_visible
= false;
184 BackColor
= SystemColors
.Window
;
185 ForeColor
= SystemColors
.WindowText
;
187 month_calendar
.DateChanged
+= new DateRangeEventHandler (MonthCalendarDateChangedHandler
);
188 month_calendar
.DateSelected
+= new DateRangeEventHandler (MonthCalendarDateSelectedHandler
);
189 month_calendar
.LostFocus
+= new EventHandler (MonthCalendarLostFocusHandler
);
190 updown_timer
.Tick
+= new EventHandler (UpDownTimerTick
);
191 KeyPress
+= new KeyPressEventHandler (KeyPressHandler
);
192 KeyDown
+= new KeyEventHandler (KeyDownHandler
);
193 LostFocus
+= new EventHandler (LostFocusHandler
);
194 MouseDown
+= new MouseEventHandler (MouseDownHandler
);
195 MouseUp
+= new MouseEventHandler (MouseUpHandler
);
196 Paint
+= new PaintEventHandler (PaintHandler
);
197 Resize
+= new EventHandler (ResizeHandler
);
198 SetStyle (ControlStyles
.UserPaint
| ControlStyles
.StandardClick
, false);
199 SetStyle (ControlStyles
.FixedHeight
, true);
200 SetStyle (ControlStyles
.Selectable
, true);
207 #region public properties
210 [EditorBrowsable(EditorBrowsableState
.Never
)]
211 public override Color BackColor
{
213 base.BackColor
= value;
216 return base.BackColor
;
221 [EditorBrowsable(EditorBrowsableState
.Never
)]
222 public override Image BackgroundImage
{
224 base.BackgroundImage
= value;
227 return base.BackgroundImage
;
233 [EditorBrowsable (EditorBrowsableState
.Never
)]
234 public override ImageLayout BackgroundImageLayout
{
236 return base.BackgroundImageLayout
;
239 base.BackgroundImageLayout
= value;
246 public Font CalendarFont
{
248 month_calendar
.Font
= value;
251 return month_calendar
.Font
;
255 public Color CalendarForeColor
{
257 month_calendar
.ForeColor
= value;
260 return month_calendar
.ForeColor
;
264 public Color CalendarMonthBackground
{
266 month_calendar
.BackColor
= value;
269 return month_calendar
.BackColor
;
273 public Color CalendarTitleBackColor
{
275 month_calendar
.TitleBackColor
= value;
278 return month_calendar
.TitleBackColor
;
282 public Color CalendarTitleForeColor
{
284 month_calendar
.TitleForeColor
= value;
287 return month_calendar
.TitleForeColor
;
291 public Color CalendarTrailingForeColor
{
293 month_calendar
.TrailingForeColor
= value;
296 return month_calendar
.TrailingForeColor
;
300 // when checked the value is grayed out
303 public bool Checked
{
305 if (is_checked
!= value) {
307 // invalidate the value inside this control
309 for (int i
= 0; i
< part_data
.Length
; i
++)
310 part_data
[i
].is_selected
= false;
311 Invalidate (date_area_rect
);
320 // the custom format string to format this control with
325 [RefreshProperties(RefreshProperties
.Repaint
)]
326 public string CustomFormat
{
328 if (custom_format
!= value) {
329 custom_format
= value;
330 if (this.Format
== DateTimePickerFormat
.Custom
) {
336 return custom_format
;
341 [EditorBrowsable (EditorBrowsableState
.Never
)]
342 protected override bool DoubleBuffered
{
344 return base.DoubleBuffered
;
347 base.DoubleBuffered
= value;
352 // which side the drop down is to be aligned on
353 [DefaultValue(LeftRightAlignment
.Left
)]
355 public LeftRightAlignment DropDownAlign
{
357 if (drop_down_align
!= value) {
358 drop_down_align
= value;
362 return drop_down_align
;
367 [EditorBrowsable(EditorBrowsableState
.Never
)]
368 public override Color ForeColor
{
370 base.ForeColor
= value;
373 return base.ForeColor
;
377 // the format of the date time picker text, default is long
378 [RefreshProperties(RefreshProperties
.Repaint
)]
379 public DateTimePickerFormat Format
{
381 if (format
!= value) {
383 RecreateHandle (); // MS recreates the handle on every format change.
385 this.OnFormatChanged (EventArgs
.Empty
);
386 // invalidate the value inside this control
387 this.Invalidate (date_area_rect
);
395 public DateTime MaxDate
{
397 if (value < min_date
) {
398 throw new ArgumentException ();
400 if (value > MaxDateTime
) {
401 throw new SystemException ();
403 if (max_date
!= value) {
406 // TODO: verify this is correct behaviour when value > max date
407 if (Value
> max_date
) {
409 // invalidate the value inside this control
410 this.Invalidate (date_area_rect
);
420 public static DateTime MaximumDateTime
{
427 public DateTime MinDate
{
429 if (value < min_date
) {
430 throw new ArgumentException ();
432 if (value < MinDateTime
) {
433 throw new SystemException ();
435 if (min_date
!= value) {
438 // TODO: verify this is correct behaviour when value > max date
439 if (Value
< min_date
) {
441 // invalidate the value inside this control
442 this.Invalidate (date_area_rect
);
451 public static DateTime MinimumDateTime
{
458 [EditorBrowsable (EditorBrowsableState
.Never
)]
459 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
461 public new Padding Padding
{
462 get { return base.Padding; }
463 set { base.Padding = value; }
467 // the prefered height to draw this control using current font
469 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
470 public int PreferredHeight
{
472 // Make it proportional
473 return (int) Math
.Ceiling (Font
.Height
* 1.5);
478 [DefaultValue (false)]
480 public virtual bool RightToLeftLayout
{
482 return right_to_left_layout
;
485 if (right_to_left_layout
!= value) {
486 right_to_left_layout
= value;
487 OnRightToLeftLayoutChanged (EventArgs
.Empty
);
493 // whether or not the check box is shown
494 [DefaultValue(false)]
495 public bool ShowCheckBox
{
497 if (show_check_box
!= value) {
498 show_check_box
= value;
499 // invalidate the value inside this control
500 this.Invalidate (date_area_rect
);
504 return show_check_box
;
508 // if true show the updown control, else popup the monthcalendar
509 [DefaultValue(false)]
510 public bool ShowUpDown
{
512 if (show_up_down
!= value) {
513 show_up_down
= value;
514 // need to invalidate the whole control
524 [EditorBrowsable(EditorBrowsableState
.Advanced
)]
525 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
526 public override string Text
{
528 DateTime parsed_value
;
530 if (value == null || value == string.Empty
) {
531 Value
= DateTime
.Now
;
532 OnTextChanged (EventArgs
.Empty
);
536 if (format
== DateTimePickerFormat
.Custom
) {
537 // TODO: if the format is a custom format we need to do a custom parse here
538 // This implementation will fail if the custom format is set to something that can
539 // be a standard datetime format string
540 // http://msdn2.microsoft.com/en-us/library/az4se3k1.aspx
541 parsed_value
= DateTime
.ParseExact (value, GetExactFormat (), null);
543 parsed_value
= DateTime
.ParseExact (value, GetExactFormat (), null);
546 if (date_value
!= parsed_value
) {
547 Value
= parsed_value
;
551 if (!IsHandleCreated
)
554 if (format
== DateTimePickerFormat
.Custom
) {
555 System
.Text
.StringBuilder result
= new System
.Text
.StringBuilder ();
556 for (int i
= 0; i
< part_data
.Length
; i
++) {
557 result
.Append(part_data
[i
].GetText(date_value
));
559 return result
.ToString ();
561 return Value
.ToString (GetExactFormat ());
567 [RefreshProperties(RefreshProperties
.All
)]
568 public DateTime Value
{
570 if (date_value
!= value) {
572 this.OnValueChanged (EventArgs
.Empty
);
573 this.Invalidate (date_area_rect
);
581 #endregion // public properties
583 #region public methods
585 // just return the text value
586 public override string ToString () {
590 #endregion // public methods
592 #region public events
593 static object CloseUpEvent
= new object ();
594 static object DropDownEvent
= new object ();
595 static object FormatChangedEvent
= new object ();
596 static object ValueChangedEvent
= new object ();
598 static object RightToLeftLayoutChangedEvent
= new object ();
601 // raised when the monthcalendar is closed
602 public event EventHandler CloseUp
{
603 add { Events.AddHandler (CloseUpEvent, value); }
604 remove { Events.RemoveHandler (CloseUpEvent, value); }
607 // raised when the monthcalendar is opened
608 public event EventHandler DropDown
{
609 add { Events.AddHandler (DropDownEvent, value); }
610 remove { Events.RemoveHandler (DropDownEvent, value); }
613 // raised when the format of the value is changed
614 public event EventHandler FormatChanged
{
615 add { Events.AddHandler (FormatChangedEvent, value); }
616 remove { Events.RemoveHandler (FormatChangedEvent, value); }
619 // raised when the date Value is changed
620 public event EventHandler ValueChanged
{
621 add { Events.AddHandler (ValueChangedEvent, value); }
622 remove { Events.RemoveHandler (ValueChangedEvent, value); }
626 [EditorBrowsable(EditorBrowsableState
.Never
)]
627 public new event EventHandler BackColorChanged
{
629 base.BackColorChanged
+= value;
633 base.BackColorChanged
-= value;
638 [EditorBrowsable(EditorBrowsableState
.Never
)]
639 public new event EventHandler BackgroundImageChanged
{
641 base.BackgroundImageChanged
+= value;
645 base.BackgroundImageChanged
-= value;
650 [EditorBrowsable (EditorBrowsableState
.Never
)]
651 public new event EventHandler BackgroundImageLayoutChanged
{
654 base.BackgroundImageLayoutChanged
+= value;
659 base.BackgroundImageLayoutChanged
-= value;
664 [EditorBrowsable (EditorBrowsableState
.Never
)]
665 public new event EventHandler Click
{
675 [EditorBrowsable (EditorBrowsableState
.Never
)]
676 public new event EventHandler DoubleClick
{
678 base.DoubleClick
+= value;
681 base.DoubleClick
-= value;
687 [EditorBrowsable(EditorBrowsableState
.Never
)]
688 public new event EventHandler ForeColorChanged
{
690 base.ForeColorChanged
+= value;
694 base.ForeColorChanged
-= value;
699 [EditorBrowsable (EditorBrowsableState
.Never
)]
700 public new event MouseEventHandler MouseClick
{
702 base.MouseClick
+= value;
705 base.MouseClick
-= value;
710 [EditorBrowsable (EditorBrowsableState
.Never
)]
711 public new event MouseEventHandler MouseDoubleClick
{
713 base.MouseDoubleClick
+= value;
716 base.MouseDoubleClick
-= value;
721 [EditorBrowsable (EditorBrowsableState
.Never
)]
722 public new event EventHandler PaddingChanged
{
725 base.PaddingChanged
+= value;
729 base.PaddingChanged
-= value;
735 [EditorBrowsable(EditorBrowsableState
.Never
)]
736 public new event PaintEventHandler Paint
{
746 public event EventHandler RightToLeftLayoutChanged
{
748 Events
.AddHandler (RightToLeftLayoutChangedEvent
, value);
751 Events
.RemoveHandler (RightToLeftLayoutChangedEvent
, value);
757 [EditorBrowsable(EditorBrowsableState
.Advanced
)]
758 public new event EventHandler TextChanged
{
760 base.TextChanged
+= value;
764 base.TextChanged
-= value;
767 #endregion // public events
769 #region protected properties
771 // not sure why we're overriding this one
772 protected override CreateParams CreateParams
{
774 return base.CreateParams
;
778 // specify the default size for this control
779 protected override Size DefaultSize
{
781 // todo actually measure this properly
782 return new Size (200, PreferredHeight
);
786 #endregion // protected properties
788 #region protected methods
790 // not sure why we're overriding this one
791 protected override AccessibleObject
CreateAccessibilityInstance () {
792 return base.CreateAccessibilityInstance ();
795 // not sure why we're overriding this one
796 protected override void CreateHandle () {
797 base.CreateHandle ();
800 // not sure why we're overriding this one
801 protected override void DestroyHandle () {
802 base.DestroyHandle ();
806 // not sure why we're overriding this one
807 protected override void Dispose (bool disposing
) {
808 updown_timer
.Dispose ();
809 base.Dispose (disposing
);
813 // find out if this key is an input key for us, depends on which date part is focused
814 protected override bool IsInputKey (Keys keyData
) {
826 // raises the CloseUp event
827 protected virtual void OnCloseUp (EventArgs eventargs
) {
828 EventHandler eh
= (EventHandler
)(Events
[CloseUpEvent
]);
830 eh (this, eventargs
);
833 // raise the drop down event
834 protected virtual void OnDropDown (EventArgs eventargs
) {
835 EventHandler eh
= (EventHandler
)(Events
[DropDownEvent
]);
837 eh (this, eventargs
);
840 protected override void OnFontChanged(EventArgs e
) {
841 // FIXME - do we need to update/invalidate/recalc our stuff?
842 month_calendar
.Font
= Font
;
843 Size
= new Size (Size
.Width
, PreferredHeight
);
845 base.OnFontChanged (e
);
848 // raises the format changed event
849 protected virtual void OnFormatChanged (EventArgs e
) {
850 EventHandler eh
= (EventHandler
)(Events
[FormatChangedEvent
]);
855 protected override void OnHandleCreated (EventArgs e
) {
856 base.OnHandleCreated(e
);
858 protected override void OnHandleDestroyed (EventArgs e
) {
859 base.OnHandleDestroyed(e
);
862 [EditorBrowsable (EditorBrowsableState
.Advanced
)]
863 protected virtual void OnRightToLeftLayoutChanged (EventArgs e
) {
864 EventHandler eh
= (EventHandler
) Events
[RightToLeftLayoutChangedEvent
];
869 // not sure why we're overriding this one
870 protected override void OnSystemColorsChanged (EventArgs e
) {
871 base.OnSystemColorsChanged (e
);
874 // raise the ValueChanged event
875 protected virtual void OnValueChanged (EventArgs eventargs
) {
876 EventHandler eh
= (EventHandler
)(Events
[ValueChangedEvent
]);
878 eh (this, eventargs
);
881 // overridden to set the bounds of this control properly
882 protected override void SetBoundsCore(int x
, int y
, int width
, int height
, BoundsSpecified specified
) {
883 // TODO: ensure I implemented the bounds core setting properly.
884 if ((specified
& BoundsSpecified
.Height
) == BoundsSpecified
.Height
||
885 (specified
& BoundsSpecified
.Size
) == BoundsSpecified
.Size
) {
886 base.SetBoundsCore (x
, y
, width
, DefaultSize
.Height
, specified
);
888 base.SetBoundsCore (x
, y
, width
, height
, specified
);
891 // need to set the rectangles for all the support internal rects
892 // this is done here as a optimisation since this is an array of rects
893 if ((specified
& BoundsSpecified
.X
) == BoundsSpecified
.X
||
894 (specified
& BoundsSpecified
.Y
) == BoundsSpecified
.Y
) {
895 // TODO set up all the datepart rects
899 // not sure why we're overriding this
900 protected override void WndProc (ref Message m
) {
901 base.WndProc (ref m
);
904 #endregion // protected methods
906 #region internal / private properties
908 // this is the region that the date and the check box is drawn on
909 internal Rectangle date_area_rect
{
911 Rectangle rect
= this.ClientRectangle
;
913 // set the space to the left of the up/down button
914 if (rect
.Width
> (up_down_width
+ 4)) {
915 rect
.Width
-= (up_down_width
+ 4);
920 // set the space to the left of the up/down button
921 // TODO make this use up down button
922 if (rect
.Width
> (SystemInformation
.VerticalScrollBarWidth
+ 4)) {
923 rect
.Width
-= SystemInformation
.VerticalScrollBarWidth
;
929 rect
.Inflate (-2, -2);
934 internal Rectangle CheckBoxRect
{
936 Rectangle retval
= new Rectangle (check_box_space
, ClientSize
.Height
/ 2 - check_box_size
/ 2,
937 check_box_size
, check_box_size
);
942 // the rectangle for the drop down arrow
943 internal Rectangle drop_down_arrow_rect
{
945 Rectangle rect
= this.ClientRectangle
;
946 rect
.X
= rect
.Right
- SystemInformation
.VerticalScrollBarWidth
- 2;
947 if (rect
.Width
> (SystemInformation
.VerticalScrollBarWidth
+ 2)) {
948 rect
.Width
= SystemInformation
.VerticalScrollBarWidth
;
950 rect
.Width
= Math
.Max (rect
.Width
- 2, 0);
953 rect
.Inflate (0, -2);
958 // the part of the date that is currently hilighted
959 internal Rectangle hilight_date_area
{
961 // TODO: put hilighted part calculation in here
962 return Rectangle
.Empty
;
968 #region internal / private methods
970 private void ResizeHandler (object sender
, EventArgs e
)
975 private void UpDownTimerTick (object sender
, EventArgs e
)
977 if (updown_timer
.Interval
== initial_timer_delay
)
978 updown_timer
.Interval
= subsequent_timer_delay
;
981 IncrementSelectedPart (-1);
982 else if (is_up_pressed
)
983 IncrementSelectedPart (1);
985 updown_timer
.Enabled
= false;
988 // calculates the maximum width
989 internal Single
CalculateMaxWidth(string format
, Graphics gr
, StringFormat string_format
)
994 Font font
= this.Font
;
1002 for (int i
= 1; i
<= 12; i
++) {
1003 text
= PartData
.GetText (Value
.AddMonths (i
), format
);
1004 size
= gr
.MeasureString (text
, font
, int.MaxValue
, string_format
);
1005 result
= Math
.Max (result
, size
.Width
);
1012 for (int i
= 1; i
<= 12; i
++) {
1013 text
= PartData
.GetText (Value
.AddDays (i
), format
);
1014 size
= gr
.MeasureString (text
, font
, int.MaxValue
, string_format
);
1015 result
= Math
.Max (result
, size
.Width
);
1020 for (int i
= 1; i
<= 12; i
++) {
1021 text
= PartData
.GetText (Value
.AddHours (i
), format
);
1022 size
= gr
.MeasureString (text
, font
, int.MaxValue
, string_format
);
1023 result
= Math
.Max (result
, size
.Width
);
1028 for (int i
= 1; i
<= 24; i
++) {
1029 text
= PartData
.GetText (Value
.AddDays (i
), format
);
1030 size
= gr
.MeasureString (text
, font
, int.MaxValue
, string_format
);
1031 result
= Math
.Max (result
, size
.Width
);
1036 for (int i
= 1; i
<= 60; i
++) {
1037 text
= PartData
.GetText (Value
.AddMinutes (i
), format
);
1038 size
= gr
.MeasureString (text
, font
, int.MaxValue
, string_format
);
1039 result
= Math
.Max (result
, size
.Width
);
1044 for (int i
= 1; i
<= 60; i
++) {
1045 text
= PartData
.GetText (Value
.AddSeconds (i
), format
);
1046 size
= gr
.MeasureString (text
, font
, int.MaxValue
, string_format
);
1047 result
= Math
.Max (result
, size
.Width
);
1052 for (int i
= 1; i
<= 2; i
++) {
1053 text
= PartData
.GetText (Value
.AddHours (i
* 12), format
);
1054 size
= gr
.MeasureString (text
, font
, int.MaxValue
, string_format
);
1055 result
= Math
.Max (result
, size
.Width
);
1061 for (int i
= 1; i
<= 10; i
++) {
1062 text
= PartData
.GetText (Value
.AddYears (i
), format
);
1063 size
= gr
.MeasureString (text
, font
, int.MaxValue
, string_format
);
1064 result
= Math
.Max (result
, size
.Width
);
1068 return gr
.MeasureString (format
, font
, int.MaxValue
, string_format
).Width
;
1072 // returns the format of the date as a string
1073 // (i.e. resolves the Format enum values to it's corresponding string format)
1074 // Why CurrentCulture and not CurrentUICulture is explained here:
1075 // http://blogs.msdn.com/michkap/archive/2007/01/11/1449754.aspx
1076 private string GetExactFormat()
1078 switch (this.format
) {
1079 case DateTimePickerFormat
.Long
:
1080 return Threading
.Thread
.CurrentThread
.CurrentCulture
.DateTimeFormat
.LongDatePattern
;
1081 case DateTimePickerFormat
.Short
:
1082 return Threading
.Thread
.CurrentThread
.CurrentCulture
.DateTimeFormat
.ShortDatePattern
;
1083 case DateTimePickerFormat
.Time
:
1084 return Threading
.Thread
.CurrentThread
.CurrentCulture
.DateTimeFormat
.LongTimePattern
;
1085 case DateTimePickerFormat
.Custom
:
1086 return this.custom_format
== null ? String
.Empty
: this.custom_format
;
1088 return Threading
.Thread
.CurrentThread
.CurrentCulture
.DateTimeFormat
.LongDatePattern
;
1092 private void CalculateFormats()
1095 System
.Text
.StringBuilder literal
= new System
.Text
.StringBuilder ();
1096 System
.Collections
.ArrayList formats
= new ArrayList ();
1097 bool is_literal
= false;
1098 char lastch
= (char) 0;
1101 real_format
= GetExactFormat ();
1103 // parse the format string
1104 for (int i
= 0; i
< real_format
.Length
; i
++)
1106 ch
= real_format
[i
];
1108 if (is_literal
&& ch
!= '\'')
1110 literal
.Append (ch
);
1124 case 'g': // Spec says nothing about g, but it seems to be treated like spaces.
1125 if (!(lastch
== ch
|| lastch
== 0) && literal
.Length
!= 0)
1127 formats
.Add (new PartData(literal
.ToString (), false));
1130 literal
.Append (ch
);
1133 if (is_literal
&& i
< real_format
.Length
- 1 && real_format
[i
+ 1] == '\'') {
1134 literal
.Append (ch
);
1138 if (literal
.Length
== 0) {
1139 is_literal
= !is_literal
;
1142 formats
.Add (new PartData (literal
.ToString (), is_literal
));
1144 is_literal
= !is_literal
;
1147 if (literal
.Length
!= 0)
1149 formats
.Add (new PartData(literal
.ToString (), false));
1152 formats
.Add (new PartData (ch
.ToString(), true));
1158 if (literal
.Length
>= 0)
1159 formats
.Add (new PartData (literal
.ToString (), is_literal
));
1161 part_data
= new PartData
[formats
.Count
];
1162 formats
.CopyTo (part_data
);
1165 private Point
CalculateDropDownLocation (Rectangle parent_control_rect
, Size child_size
, bool align_left
)
1167 // default bottom left
1168 Point location
= new Point(parent_control_rect
.Left
+ 5, parent_control_rect
.Bottom
);
1169 // now adjust the alignment
1171 location
.X
= parent_control_rect
.Right
- child_size
.Width
;
1174 Point screen_location
= PointToScreen (location
);
1175 Rectangle working_area
= Screen
.FromControl(this).WorkingArea
;
1176 // now adjust if off the right side of the screen
1177 if (screen_location
.X
< working_area
.X
) {
1178 screen_location
.X
= working_area
.X
;
1180 // now adjust if it should be displayed above control
1181 if (screen_location
.Y
+ child_size
.Height
> working_area
.Bottom
) {
1182 screen_location
.Y
-= (parent_control_rect
.Height
+ child_size
.Height
);
1185 // since the parent of the month calendar is the form, adjust accordingly.
1186 screen_location
= month_calendar
.Parent
.PointToClient(screen_location
);
1188 return screen_location
;
1191 // actually draw this control
1192 internal void Draw (Rectangle clip_rect
, Graphics dc
)
1194 ThemeEngine
.Current
.DrawDateTimePicker (dc
, clip_rect
, this);
1197 // drop the calendar down
1198 internal void DropDownMonthCalendar ()
1200 // ensure the right date is set for the month_calendar
1201 month_calendar
.SetDate (this.date_value
);
1202 // get a rectangle that has the dimensions of the text area,
1203 // but the height of the dtp control.
1204 Rectangle align_area
= this.date_area_rect
;
1205 align_area
.Y
= this.ClientRectangle
.Y
;
1206 align_area
.Height
= this.ClientRectangle
.Height
;
1208 month_calendar
.Parent
= this.FindForm ();
1209 // establish the month calendar's location
1210 month_calendar
.Location
= CalculateDropDownLocation (
1212 month_calendar
.Size
,
1213 (this.DropDownAlign
== LeftRightAlignment
.Left
));
1214 month_calendar
.Show ();
1215 month_calendar
.Focus ();
1216 month_calendar
.Capture
= true;
1218 // fire any registered events
1219 // XXX should this just call OnDropDown?
1220 EventHandler eh
= (EventHandler
)(Events
[DropDownEvent
]);
1222 eh (this, EventArgs
.Empty
);
1225 // hide the month calendar
1226 internal void HideMonthCalendar ()
1228 this.is_drop_down_visible
= false;
1229 Invalidate (drop_down_arrow_rect
);
1230 month_calendar
.Capture
= false;
1231 if (month_calendar
.Visible
) {
1232 month_calendar
.Hide ();
1236 private int GetSelectedPartIndex()
1238 for (int i
= 0; i
< part_data
.Length
; i
++)
1240 if (part_data
[i
].is_selected
&& !part_data
[i
].is_literal
)
1246 private void IncrementSelectedPart(int delta
)
1248 int selected_index
= GetSelectedPartIndex();
1250 if (selected_index
== -1) {
1254 switch (part_data
[selected_index
].value)
1257 case "dd": // number day formats
1260 SetPart(DateTime
.DaysInMonth(Value
.Year
, Value
.Month
), 'd');
1262 SetPart(Value
.Day
+ delta
, 'd');
1264 if (Value
.Day
== DateTime
.DaysInMonth(Value
.Year
, Value
.Month
))
1267 SetPart(Value
.Day
+ delta
, 'd') ;
1271 case "dddd": // text day formats
1272 Value
= Value
.AddDays(delta
);
1277 case "HH": // hour formats
1278 SetPart(Value
.Hour
+ delta
, 'h');
1281 case "mm": // minute formats
1282 SetPart(Value
.Minute
+ delta
, 'm');
1287 case "MMMM": // month formats
1288 SetPart(Value
.Month
+ delta
, 'M');
1291 case "ss": // second format
1292 SetPart(Value
.Second
+ delta
, 's');
1295 case "tt": // AM / PM specifier
1296 SetPart(Value
.Hour
+ delta
* 12, 'h');
1302 SetPart(Value
.Year
+ delta
, 'y');
1307 private void SelectNextPart()
1310 if (is_checkbox_selected
) {
1311 for (int i
= 0; i
< part_data
.Length
; i
++)
1313 if (!part_data
[i
].is_literal
)
1315 is_checkbox_selected
= false;
1316 part_data
[i
].is_selected
= true;
1322 selected_index
= GetSelectedPartIndex();
1323 if (selected_index
>= 0)
1324 part_data
[selected_index
].is_selected
= false;
1325 for (int i
= selected_index
+ 1; i
< part_data
.Length
; i
++)
1327 if (!part_data
[i
].is_literal
)
1329 part_data
[i
].is_selected
= true;
1334 if (GetSelectedPartIndex() == -1)
1335 { // if no part was found before the end, look from the beginning
1338 is_checkbox_selected
= true;
1343 for (int i
= 0; i
<= selected_index
; i
++)
1345 if (!part_data
[i
].is_literal
)
1347 part_data
[i
].is_selected
= true;
1358 private void SelectPreviousPart()
1360 if (is_checkbox_selected
)
1362 for (int i
= part_data
.Length
- 1; i
>= 0; i
--)
1364 if (!part_data
[i
].is_literal
)
1366 is_checkbox_selected
= false;
1367 part_data
[i
].is_selected
= true;
1375 int selected_index
= GetSelectedPartIndex();
1377 if (selected_index
>= 0)
1378 part_data
[selected_index
].is_selected
= false;
1380 for (int i
= selected_index
- 1; i
>= 0; i
--)
1382 if (!part_data
[i
].is_literal
)
1384 part_data
[i
].is_selected
= true;
1389 if (GetSelectedPartIndex() == -1)
1390 { // if no part was found before the beginning, look from the end
1393 is_checkbox_selected
= true;
1398 for (int i
= part_data
.Length
- 1; i
>= selected_index
; i
--)
1400 if (!part_data
[i
].is_literal
)
1402 part_data
[i
].is_selected
= true;
1412 // raised by key down events.
1413 private void KeyDownHandler(object sender
, KeyEventArgs e
)
1420 if (ShowCheckBox
&& Checked
== false)
1422 IncrementSelectedPart(1);
1429 if (ShowCheckBox
&& Checked
== false)
1431 IncrementSelectedPart(-1);
1436 {// select the next part to the left
1437 if (ShowCheckBox
&& Checked
== false)
1439 SelectPreviousPart();
1444 {// select the next part to the right
1445 if (ShowCheckBox
&& Checked
== false)
1452 if (!is_drop_down_visible
)
1453 DropDownMonthCalendar();
1458 // raised by any key down events
1459 private void KeyPressHandler (object sender
, KeyPressEventArgs e
)
1461 switch (e
.KeyChar
) {
1463 if (is_checkbox_selected
)
1478 int number
= e
.KeyChar
- (int) '0';
1479 int selected_index
= GetSelectedPartIndex();
1480 if (selected_index
== -1)
1482 if (!part_data
[selected_index
].is_numeric_format
)
1484 switch (part_data
[selected_index
].value)
1488 int newDay
= Value
.Day
* 10 + number
;
1489 if (DateTime
.DaysInMonth(Value
.Year
, Value
.Month
) < newDay
)
1491 SetPart(newDay
, 'd');
1495 int newMonth
= Value
.Month
* 10 + number
;
1498 SetPart(newMonth
, 'M');
1503 int newYear
= Value
.Year
* 10 + number
;
1506 SetPart(newYear
, 'y');
1512 int newHour
= Value
.Hour
* 10 + number
;
1515 SetPart(newHour
, 'h');
1519 int newMinute
= Value
.Minute
* 10 + number
;
1520 if (newMinute
>= 60)
1522 SetPart(newMinute
, 'm');
1526 int newSecond
= Value
.Second
* 10 + number
;
1527 if (newSecond
>= 60)
1529 SetPart(newSecond
, 's');
1540 // set the specified part of the date to the specified value
1541 private void SetPart(int value, char part
)
1545 case 's': // seconds
1549 if (value >= 0 && value <= 59)
1550 Value
= new DateTime(Value
.Year
, Value
.Month
, Value
.Day
, Value
.Hour
, Value
.Minute
, value, Value
.Millisecond
);
1552 case 'm': // minutes
1556 if (value >= 0 && value <= 59)
1557 Value
= new DateTime(Value
.Year
, Value
.Month
, Value
.Day
, Value
.Hour
, value, Value
.Second
, Value
.Millisecond
);
1564 if (value >= 0 && value <= 23)
1565 Value
= new DateTime(Value
.Year
, Value
.Month
, Value
.Day
, value, Value
.Minute
, Value
.Second
, Value
.Millisecond
);
1568 int max_days
= DateTime
.DaysInMonth(Value
.Year
, Value
.Month
);
1569 if (value > max_days
)
1570 Value
= new DateTime(Value
.Year
, Value
.Month
, max_days
, Value
.Hour
, Value
.Minute
, Value
.Second
, Value
.Millisecond
);
1571 if (value >= 1 && value <= 31)
1572 Value
= new DateTime(Value
.Year
, Value
.Month
, value, Value
.Hour
, Value
.Minute
, Value
.Second
, Value
.Millisecond
);
1578 if (value >= 1 && value <= 12)
1579 Value
= new DateTime(Value
.Year
, value, Value
.Day
, Value
.Hour
, Value
.Minute
, Value
.Second
, Value
.Millisecond
);
1583 if (value > 0 && value <= 9999)
1584 Value
= new DateTime(value, Value
.Month
, Value
.Day
, Value
.Hour
, Value
.Minute
, Value
.Second
, Value
.Millisecond
);
1589 // if we loose focus deselect any selected parts.
1590 private void LostFocusHandler (object sender
, EventArgs e
)
1592 int selected_index
= GetSelectedPartIndex ();
1593 if (selected_index
!= -1)
1595 part_data
[selected_index
].is_selected
= false;
1596 Rectangle invalidate_rect
= Rectangle
.Ceiling (part_data
[selected_index
].drawing_rectangle
);
1597 invalidate_rect
.Inflate (2, 2);
1598 Invalidate (invalidate_rect
);
1600 else if (is_checkbox_selected
)
1602 is_checkbox_selected
= false;
1603 Invalidate (CheckBoxRect
);
1607 // if month calendar looses focus and the drop down is up, then close it
1608 private void MonthCalendarLostFocusHandler(object sender
, EventArgs e
)
1610 if (is_drop_down_visible
&& !month_calendar
.Focused
)
1612 //this.HideMonthCalendar();
1613 //This is handled from the monthcalender itself,
1614 //it may loose focus, but still has to be visible,
1615 //for instance when the context menu is displayed.
1620 private void MonthCalendarDateChangedHandler (object sender
, DateRangeEventArgs e
)
1622 if (month_calendar
.Visible
)
1623 this.Value
= e
.Start
.Date
.Add (this.Value
.TimeOfDay
);
1626 // fired when a user clicks on the month calendar to select a date
1627 private void MonthCalendarDateSelectedHandler (object sender
, DateRangeEventArgs e
)
1629 this.HideMonthCalendar ();
1633 private void MouseUpHandler(object sender
, MouseEventArgs e
)
1637 if (is_up_pressed
|| is_down_pressed
)
1639 updown_timer
.Enabled
= false;
1640 is_up_pressed
= false;
1641 is_down_pressed
= false;
1642 Invalidate (drop_down_arrow_rect
);
1647 // to check if the mouse has come down on this control
1648 private void MouseDownHandler (object sender
, MouseEventArgs e
)
1650 // Only left clicks are handled.
1651 if (e
.Button
!= MouseButtons
.Left
)
1654 is_checkbox_selected
= false;
1656 if (ShowCheckBox
&& CheckBoxRect
.Contains(e
.X
, e
.Y
))
1658 is_checkbox_selected
= true;
1664 if (ShowUpDown
&& drop_down_arrow_rect
.Contains (e
.X
, e
.Y
))
1666 if (!(ShowCheckBox
&& Checked
== false))
1668 if (e
.Y
< this.Height
/ 2) {
1669 is_up_pressed
= true;
1670 is_down_pressed
= false;
1671 IncrementSelectedPart (1);
1673 is_up_pressed
= false;
1674 is_down_pressed
= true;
1675 IncrementSelectedPart (-1);
1677 Invalidate (drop_down_arrow_rect
);
1678 updown_timer
.Interval
= initial_timer_delay
;
1679 updown_timer
.Enabled
= true;
1681 } else if (is_drop_down_visible
== false && drop_down_arrow_rect
.Contains (e
.X
, e
.Y
)) {
1682 is_drop_down_visible
= true;
1685 Invalidate (drop_down_arrow_rect
);
1686 DropDownMonthCalendar ();
1688 // mouse down on this control anywhere else collapses it
1689 if (is_drop_down_visible
) {
1690 HideMonthCalendar ();
1693 if (!(ShowCheckBox
&& Checked
== false))
1695 // go through the parts to see if the click is in any of them
1696 bool invalidate_afterwards
= false;
1697 for (int i
= 0; i
< part_data
.Length
; i
++) {
1698 bool old
= part_data
[i
].is_selected
;
1700 if (part_data
[i
].is_literal
)
1703 if (part_data
[i
].drawing_rectangle
.Contains (e
.X
, e
.Y
)) {
1704 part_data
[i
].is_selected
= true;
1706 part_data
[i
].is_selected
= false;
1708 if (old
!= part_data
[i
].is_selected
)
1709 invalidate_afterwards
= true;
1711 if (invalidate_afterwards
)
1719 // paint this control now
1720 private void PaintHandler (object sender
, PaintEventArgs pe
) {
1721 if (Width
<= 0 || Height
<= 0 || Visible
== false)
1724 Draw (pe
.ClipRectangle
, pe
.Graphics
);
1729 #region internal classes
1730 internal class PartData
1732 internal string value;
1733 internal bool is_literal
;
1734 internal bool is_selected
;
1735 internal RectangleF drawing_rectangle
;
1737 internal bool is_numeric_format
1769 internal PartData(string value, bool is_literal
)
1772 this.is_literal
= is_literal
;
1775 // calculate the string to show for this data
1776 internal string GetText(DateTime date
)
1781 return GetText (date
, value);
1785 static internal string GetText(DateTime date
, string format
)
1787 if (format
.StartsWith ("g"))
1789 else if (format
.Length
== 1)
1790 return date
.ToString ("%" + format
);
1792 return date
.ToString (format
);