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 Novell, Inc.
23 // John BouAntoun jba-mono@optusnet.com.au
26 // - implement custom formatting of the date time value
27 // - implement any behaviour associate with UseUpDown (painting, key and mouse)
28 // - implement key processing and responding
29 // - fix MonthCalendar Popdown on form move
30 // - wire in all events from monthcalendar
35 using System.Collections;
36 using System.ComponentModel;
37 using System.Windows.Forms;
39 namespace System.Windows.Forms {
40 [DefaultEvent("ValueChanged")]
41 [DefaultProperty("Value")]
42 [Designer("System.Windows.Forms.Design.DateTimePickerDesigner, " + Consts.AssemblySystem_Design)]
43 public class DateTimePicker : Control {
45 #region Public variables
47 // this class has to have the specified hour, minute and second, as it says in msdn
48 //public static readonly DateTime MaxDateTime = DateTime.Parse ("31 December 9998");//, 23:59:59");
49 public static readonly DateTime MaxDateTime = new DateTime (9998, 12, 31, 23, 59, 59);
50 public static readonly DateTime MinDateTime = new DateTime (1753, 1, 1);
52 #endregion // Public variables
54 #region Local variables
56 protected static readonly Color DefaultMonthBackColor = ThemeEngine.Current.ColorWindow;
57 protected static readonly Color DefaultTitleBackColor = ThemeEngine.Current.ColorActiveTitle;
58 protected static readonly Color DefaultTitleForeColor = ThemeEngine.Current.ColorTitleText;
59 protected static readonly Color DefaultTrailingForeColor = Color.Gray;
61 internal MonthCalendar month_calendar;
64 LeftRightAlignment drop_down_align;
65 DateTimePickerFormat format;
73 // variables used for drawing and such
74 internal int up_down_width;
75 internal bool is_drop_down_visible;
77 #endregion // Local variables
79 #region public constructors
81 // only public constructor
82 public DateTimePicker () {
84 // initialise the month calendar
85 month_calendar = new MonthCalendar (this);
86 month_calendar.CalendarDimensions = new Size (1, 1);
87 month_calendar.MaxSelectionCount = 1;
88 month_calendar.ForeColor = Control.DefaultForeColor;
89 month_calendar.BackColor = DefaultMonthBackColor;
90 month_calendar.TitleBackColor = DefaultTitleBackColor;
91 month_calendar.TitleForeColor = DefaultTitleForeColor;
92 month_calendar.TrailingForeColor = DefaultTrailingForeColor;
93 month_calendar.Visible = false;
96 // initialise other variables
98 custom_format = string.Empty;
99 drop_down_align = LeftRightAlignment.Left;
100 format = DateTimePickerFormat.Long;
101 max_date = MaxDateTime;
102 min_date = MinDateTime;
103 show_check_box = false;
104 show_up_down = false;
105 date_value = DateTime.Now;
106 text = FormatValue ();
109 is_drop_down_visible = false;
111 month_calendar.DateSelected += new DateRangeEventHandler (MonthCalendarDateSelectedHandler);
112 KeyPress += new KeyPressEventHandler (KeyPressHandler);
113 LostFocus += new EventHandler (LostFocusHandler);
114 MouseDown += new MouseEventHandler (MouseDownHandler);
115 Paint += new PaintEventHandler (PaintHandler);
121 #region public properties
123 // no reason why this is overridden
125 [EditorBrowsable(EditorBrowsableState.Never)]
126 public Color BackColor {
128 base.BackColor = value;
131 return base.BackColor;
135 // no reason why this is overridden
137 [EditorBrowsable(EditorBrowsableState.Never)]
138 public override Image BackgroundImage {
140 base.BackgroundImage = value;
143 return base.BackgroundImage;
149 public Font CalendarFont {
151 month_calendar.Font = value;
154 return month_calendar.Font;
158 public Color CalendarForeColor {
160 month_calendar.ForeColor = value;
163 return month_calendar.ForeColor;
167 public Color CalendarMonthBackground {
169 month_calendar.BackColor = value;
172 return month_calendar.BackColor;
176 public Color CalendarTitleBackColor {
178 month_calendar.TitleBackColor = value;
181 return month_calendar.TitleBackColor;
185 public Color CalendarTitleForeColor {
187 month_calendar.TitleForeColor = value;
190 return month_calendar.TitleForeColor;
194 public Color CalendarTrailingForeColor {
196 month_calendar.TrailingForeColor = value;
199 return month_calendar.TrailingForeColor;
203 // when checked the value is grayed out
206 public bool Checked {
208 if (is_checked != value) {
210 // invalidate the value inside this control
211 this.Invalidate (date_area_rect);
219 // the custom format string to format this control with
221 [RefreshProperties(RefreshProperties.Repaint)]
222 public string CustomFormat {
224 if (custom_format != value) {
225 custom_format = value;
226 if (this.Format == DateTimePickerFormat.Custom) {
227 // invalidate the value inside this control
228 this.Invalidate (date_area_rect);
233 return custom_format;
237 // which side the drop down is to be aligned on
238 [DefaultValue(LeftRightAlignment.Left)]
240 public LeftRightAlignment DropDownAlign {
242 if (drop_down_align != value) {
243 drop_down_align = value;
247 return drop_down_align;
252 [EditorBrowsable(EditorBrowsableState.Never)]
253 public override Color ForeColor {
255 base.ForeColor = value;
258 return base.ForeColor;
262 // the format of the date time picker text, default is long
263 [RefreshProperties(RefreshProperties.Repaint)]
264 public DateTimePickerFormat Format {
266 if (format != value) {
268 this.OnFormatChanged (EventArgs.Empty);
269 // invalidate the value inside this control
270 this.Invalidate (date_area_rect);
278 public DateTime MaxDate {
280 if (value < min_date) {
281 throw new ArgumentException ();
283 if (value > MaxDateTime) {
284 throw new SystemException ();
286 if (max_date != value) {
289 // TODO: verify this is correct behaviour when value > max date
290 if (Value > max_date) {
292 // invalidate the value inside this control
293 this.Invalidate (date_area_rect);
302 public DateTime MinDate {
304 if (value < min_date) {
305 throw new ArgumentException ();
307 if (value < MinDateTime) {
308 throw new SystemException ();
310 if (min_date != value) {
313 // TODO: verify this is correct behaviour when value > max date
314 if (Value < min_date) {
316 // invalidate the value inside this control
317 this.Invalidate (date_area_rect);
326 // the prefered height to draw this control using current font
328 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
329 public int PreferredHeight {
331 return this.Font.Height + 7;
335 // whether or not the check box is shown
336 [DefaultValue(false)]
337 public bool ShowCheckBox {
339 if (show_check_box != value) {
340 show_check_box = value;
341 // invalidate the value inside this control
342 this.Invalidate (date_area_rect);
346 return show_check_box;
350 // if true show the updown control, else popup the monthcalendar
351 [DefaultValue(false)]
352 public bool ShowUpDown {
354 if (show_up_down != value) {
355 show_up_down = value;
356 // need to invalidate the whole control
366 [EditorBrowsable(EditorBrowsableState.Advanced)]
367 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
370 DateTime parsed_value = DateTime.Parse (value);
371 if (date_value != parsed_value) {
372 Value = parsed_value;
381 [RefreshProperties(RefreshProperties.All)]
382 public DateTime Value {
384 if (date_value != value) {
386 text = FormatValue ();
387 this.OnValueChanged (EventArgs.Empty);
388 this.Invalidate (date_area_rect);
396 #endregion // public properties
398 #region public methods
400 // just return the text value
401 public override string ToString () {
405 #endregion // public methods
407 #region public events
409 // raised when the monthcalendar is closed
410 public event EventHandler CloseUp;
412 // raised when the monthcalendar is opened
413 public event EventHandler DropDown;
415 // raised when the format of the value is changed
416 public event EventHandler FormatChanged;
418 // raised when the date Value is changed
419 public event EventHandler ValueChanged;
421 #endregion // public events
423 #region protected properties
425 // not sure why we're overriding this one
426 protected override CreateParams CreateParams {
428 return base.CreateParams;
432 // specify the default size for this control
433 protected override Size DefaultSize {
435 // todo actually measure this properly
436 return new Size (200, PreferredHeight);
440 #endregion // protected properties
442 #region protected methods
444 // not sure why we're overriding this one
445 protected override AccessibleObject CreateAccessibilityInstance () {
446 return base.CreateAccessibilityInstance ();
449 // not sure why we're overriding this one
450 protected override void CreateHandle () {
451 base.CreateHandle ();
454 // not sure why we're overriding this one
455 protected override void DestroyHandle () {
456 base.DestroyHandle ();
459 // not sure why we're overriding this one
460 protected override void Dispose (bool disposing) {
461 base.Dispose (disposing);
464 // find out if this key is an input key for us, depends on which date part is focused
465 protected override bool IsInputKey (Keys keyData) {
466 // TODO: fix this implementation of IsInputKey
470 // raises the CloseUp event
471 protected virtual void OnCloseUp (EventArgs eventargs) {
472 if (this.CloseUp != null) {
473 this.CloseUp (this, eventargs);
477 // raise the drop down event
478 protected virtual void OnDropDown (EventArgs eventargs) {
479 if (this.DropDown != null) {
480 this.DropDown (this, eventargs);
484 // raises the format changed event
485 protected virtual void OnFormatChanged (EventArgs e) {
486 if (this.FormatChanged != null) {
487 this.FormatChanged (this, e);
491 // not sure why we're overriding this one
492 protected override void OnSystemColorsChanged (EventArgs e) {
493 base.OnSystemColorsChanged (e);
496 // raise the ValueChanged event
497 protected virtual void OnValueChanged (EventArgs eventargs) {
498 if (this.ValueChanged != null) {
499 this.ValueChanged (this, eventargs);
503 // overridden to set the bounds of this control properly
504 protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
505 // TODO: ensure I implemented the bounds core setting properly.
506 if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height ||
507 (specified & BoundsSpecified.Size) == BoundsSpecified.Size) {
508 base.SetBoundsCore (x, y, width, DefaultSize.Height, specified);
510 base.SetBoundsCore (x, y, width, height, specified);
513 // need to set the rectangles for all the support internal rects
514 // this is done here as a optimisation since this is an array of rects
515 if ((specified & BoundsSpecified.X) == BoundsSpecified.X ||
516 (specified & BoundsSpecified.Y) == BoundsSpecified.Y) {
517 // TODO set up all the datepart rects
521 // not sure why we're overriding this
522 protected override void WndProc (ref Message m) {
523 base.WndProc (ref m);
526 #endregion // protected methods
528 #region internal / private properties
530 // this is the region that the date and the check box is drawn on
531 internal Rectangle date_area_rect {
533 Rectangle rect = this.ClientRectangle;
535 // set the space to the left of the up/down button
536 if (rect.Width > (up_down_width + 4)) {
537 rect.Width -= (up_down_width + 4);
542 // set the space to the left of the up/down button
543 // TODO make this use up down button
544 if (rect.Width > (SystemInformation.VerticalScrollBarWidth + 4)) {
545 rect.Width -= SystemInformation.VerticalScrollBarWidth;
551 rect.Inflate (-2, -2);
556 // the rectangle for the drop down arrow
557 internal Rectangle drop_down_arrow_rect {
559 Rectangle rect = this.ClientRectangle;
560 rect.X = rect.Right - SystemInformation.VerticalScrollBarWidth - 2;
561 if (rect.Width > (SystemInformation.VerticalScrollBarWidth + 2)) {
562 rect.Width = SystemInformation.VerticalScrollBarWidth;
564 rect.Width = Math.Max (rect.Width - 2, 0);
567 rect.Inflate (0, -2);
572 // the part of the date that is currently hilighted
573 internal Rectangle hilight_date_area {
575 // TODO: put hilighted part calculation in here
576 return Rectangle.Empty;
582 #region internal / private methods
584 [MonoTODO("Fix Dropdown location when System.Windows.Forms.Screen gets added")]
585 private Point CalculateDropDownLocation (Rectangle parent_control_rect, Size child_size, bool align_left)
587 // default bottom left
588 Point location = new Point(parent_control_rect.Left + 5, parent_control_rect.Bottom);
589 // now adjust the alignment
591 location.X = parent_control_rect.Right - child_size.Width;
594 Point screen_location = PointToScreen (location);
595 // TODO: enable this part when screen comes into the classes
597 Rectangle working_area = Screen.FromControl(this).WorkingArea;
598 // now adjust if off the right side of the screen
599 if (screen_location.X < working_area.X) {
600 screen_location.X = working_area.X;
602 // now adjust if it should be displayed above control
603 if (screen_location.Y + child_size.Height > working_area.Bottom) {
604 screen_location.Y -= (parent_control_rect.Height + child_size.Height);
607 return screen_location;
610 // actually draw this control
611 internal void Draw (Rectangle clip_rect)
613 ThemeEngine.Current.DrawDateTimePicker(DeviceContext, clip_rect, this);
616 // drop the calendar down
617 internal void DropDownMonthCalendar ()
619 // ensure the right date is set for the month_calendar
620 month_calendar.SetDate (this.date_value);
621 // get a rectangle that has the dimensions of the text area,
622 // but the height of the dtp control.
623 Rectangle align_area = this.date_area_rect;
624 align_area.Y = this.ClientRectangle.Y;
625 align_area.Height = this.ClientRectangle.Height;
627 // establish the month calendar's location
628 month_calendar.Location = CalculateDropDownLocation (
631 (this.DropDownAlign == LeftRightAlignment.Left));
633 month_calendar.Show ();
634 month_calendar.Focus ();
636 // fire any registered events
637 if (this.DropDown != null) {
638 this.DropDown (this, EventArgs.Empty);
642 // hide the month calendar
643 internal void HideMonthCalendar ()
645 this.is_drop_down_visible = false;
646 Invalidate (drop_down_arrow_rect);
647 if (month_calendar.Visible) {
648 month_calendar.Hide ();
652 // raised by any key down events
653 private void KeyPressHandler (object sender, KeyPressEventArgs e) {
661 // if we lose focus and the drop down is up, then close it
662 private void LostFocusHandler (object sender, EventArgs e)
664 if (is_drop_down_visible && !month_calendar.Focused) {
665 this.HideMonthCalendar ();
669 // fired when a user clicks on the month calendar to select a date
670 private void MonthCalendarDateSelectedHandler (object sender, DateRangeEventArgs e)
672 this.Value = e.Start.Date.Add (this.Value.TimeOfDay);
673 this.HideMonthCalendar ();
675 System.Console.WriteLine("MonthCalendarDateSelectedHandler");
678 // to check if the mouse has come down on this control
679 private void MouseDownHandler (object sender, MouseEventArgs e)
683 // TODO: Process clicking for UPDown
685 if (is_drop_down_visible == false && drop_down_arrow_rect.Contains (e.X, e.Y)) {
686 is_drop_down_visible = true;
687 Invalidate (drop_down_arrow_rect);
688 DropDownMonthCalendar ();
690 // mouse down on this control anywhere else collapses it
691 if (is_drop_down_visible) {
692 System.Console.WriteLine("hiding cause of mouse down");
693 HideMonthCalendar ();
700 // paint this control now
701 private void PaintHandler (object sender, PaintEventArgs pe) {
702 if (Width <= 0 || Height <= 0 || Visible == false)
705 Draw (pe.ClipRectangle);
706 pe.Graphics.DrawImage (ImageBuffer, 0, 0);
709 private string FormatValue () {
710 string ret_value = string.Empty;
712 case DateTimePickerFormat.Custom:
713 // TODO implement custom text formatting
714 ret_value = date_value.ToString ();
716 case DateTimePickerFormat.Short:
717 ret_value = date_value.ToShortDateString ();
719 case DateTimePickerFormat.Time:
720 ret_value = date_value.ToShortDateString ();
723 ret_value = date_value.ToLongDateString ();