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, "System.ComponentModel.Design.IDesigner")]
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 = new DateTime (9998, 12, 31, 23, 59, 59);
49 public static readonly DateTime MinDateTime = new DateTime (1753, 1, 1);
51 #endregion // Public variables
53 #region Local variables
55 protected static readonly Color DefaultMonthBackColor = ThemeEngine.Current.ColorWindow;
56 protected static readonly Color DefaultTitleBackColor = ThemeEngine.Current.ColorActiveCaption;
57 protected static readonly Color DefaultTitleForeColor = ThemeEngine.Current.ColorActiveCaptionText;
58 protected static readonly Color DefaultTrailingForeColor = Color.Gray;
60 internal MonthCalendar month_calendar;
63 LeftRightAlignment drop_down_align;
64 DateTimePickerFormat format;
72 // variables used for drawing and such
73 internal int up_down_width;
74 internal bool is_drop_down_visible;
76 #endregion // Local variables
78 #region public constructors
80 // only public constructor
81 public DateTimePicker () {
83 // initialise the month calendar
84 month_calendar = new MonthCalendar (this);
85 month_calendar.CalendarDimensions = new Size (1, 1);
86 month_calendar.MaxSelectionCount = 1;
87 month_calendar.ForeColor = Control.DefaultForeColor;
88 month_calendar.BackColor = DefaultMonthBackColor;
89 month_calendar.TitleBackColor = DefaultTitleBackColor;
90 month_calendar.TitleForeColor = DefaultTitleForeColor;
91 month_calendar.TrailingForeColor = DefaultTrailingForeColor;
92 month_calendar.Visible = false;
95 // initialise other variables
97 custom_format = string.Empty;
98 drop_down_align = LeftRightAlignment.Left;
99 format = DateTimePickerFormat.Long;
100 max_date = MaxDateTime;
101 min_date = MinDateTime;
102 show_check_box = false;
103 show_up_down = false;
104 date_value = DateTime.Now;
105 text = FormatValue ();
108 is_drop_down_visible = false;
110 month_calendar.DateSelected += new DateRangeEventHandler (MonthCalendarDateSelectedHandler);
111 KeyPress += new KeyPressEventHandler (KeyPressHandler);
112 // LostFocus += new EventHandler (LostFocusHandler);
113 MouseDown += new MouseEventHandler (MouseDownHandler);
114 Paint += new PaintEventHandler (PaintHandler);
116 SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick, false);
117 SetStyle (ControlStyles.FixedHeight, true);
122 #region public properties
124 // no reason why this is overridden
126 [EditorBrowsable(EditorBrowsableState.Never)]
127 public Color BackColor {
129 base.BackColor = value;
132 return base.BackColor;
136 // no reason why this is overridden
138 [EditorBrowsable(EditorBrowsableState.Never)]
139 public override Image BackgroundImage {
141 base.BackgroundImage = value;
144 return base.BackgroundImage;
150 public Font CalendarFont {
152 month_calendar.Font = value;
155 return month_calendar.Font;
159 public Color CalendarForeColor {
161 month_calendar.ForeColor = value;
164 return month_calendar.ForeColor;
168 public Color CalendarMonthBackground {
170 month_calendar.BackColor = value;
173 return month_calendar.BackColor;
177 public Color CalendarTitleBackColor {
179 month_calendar.TitleBackColor = value;
182 return month_calendar.TitleBackColor;
186 public Color CalendarTitleForeColor {
188 month_calendar.TitleForeColor = value;
191 return month_calendar.TitleForeColor;
195 public Color CalendarTrailingForeColor {
197 month_calendar.TrailingForeColor = value;
200 return month_calendar.TrailingForeColor;
204 // when checked the value is grayed out
207 public bool Checked {
209 if (is_checked != value) {
211 // invalidate the value inside this control
212 this.Invalidate (date_area_rect);
220 // the custom format string to format this control with
222 [RefreshProperties(RefreshProperties.Repaint)]
223 public string CustomFormat {
225 if (custom_format != value) {
226 custom_format = value;
227 if (this.Format == DateTimePickerFormat.Custom) {
228 // TODO: change the text value of the dtp
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 // TODO: if the format is a custom format we need to do a custom parse here
371 DateTime parsed_value = DateTime.Parse (value);
372 if (date_value != parsed_value) {
373 Value = parsed_value;
375 text = FormatValue ();
383 [RefreshProperties(RefreshProperties.All)]
384 public DateTime Value {
386 if (date_value != value) {
388 text = FormatValue ();
389 this.OnValueChanged (EventArgs.Empty);
390 this.Invalidate (date_area_rect);
398 #endregion // public properties
400 #region public methods
402 // just return the text value
403 public override string ToString () {
407 #endregion // public methods
409 #region public events
411 // raised when the monthcalendar is closed
412 public event EventHandler CloseUp;
414 // raised when the monthcalendar is opened
415 public event EventHandler DropDown;
417 // raised when the format of the value is changed
418 public event EventHandler FormatChanged;
420 // raised when the date Value is changed
421 public event EventHandler ValueChanged;
423 #endregion // public events
425 #region protected properties
427 // not sure why we're overriding this one
428 protected override CreateParams CreateParams {
430 return base.CreateParams;
434 // specify the default size for this control
435 protected override Size DefaultSize {
437 // todo actually measure this properly
438 return new Size (200, PreferredHeight);
442 #endregion // protected properties
444 #region protected methods
446 // not sure why we're overriding this one
447 protected override AccessibleObject CreateAccessibilityInstance () {
448 return base.CreateAccessibilityInstance ();
451 // not sure why we're overriding this one
452 protected override void CreateHandle () {
453 base.CreateHandle ();
456 // not sure why we're overriding this one
457 protected override void DestroyHandle () {
458 base.DestroyHandle ();
461 // not sure why we're overriding this one
462 protected override void Dispose (bool disposing) {
463 base.Dispose (disposing);
466 // find out if this key is an input key for us, depends on which date part is focused
467 protected override bool IsInputKey (Keys keyData) {
468 // TODO: fix this implementation of IsInputKey
472 // raises the CloseUp event
473 protected virtual void OnCloseUp (EventArgs eventargs) {
474 if (this.CloseUp != null) {
475 this.CloseUp (this, eventargs);
479 // raise the drop down event
480 protected virtual void OnDropDown (EventArgs eventargs) {
481 if (this.DropDown != null) {
482 this.DropDown (this, eventargs);
486 // raises the format changed event
487 protected virtual void OnFormatChanged (EventArgs e) {
488 if (this.FormatChanged != null) {
489 this.FormatChanged (this, e);
493 // not sure why we're overriding this one
494 protected override void OnSystemColorsChanged (EventArgs e) {
495 base.OnSystemColorsChanged (e);
498 // raise the ValueChanged event
499 protected virtual void OnValueChanged (EventArgs eventargs) {
500 if (this.ValueChanged != null) {
501 this.ValueChanged (this, eventargs);
505 // overridden to set the bounds of this control properly
506 protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
507 // TODO: ensure I implemented the bounds core setting properly.
508 if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height ||
509 (specified & BoundsSpecified.Size) == BoundsSpecified.Size) {
510 base.SetBoundsCore (x, y, width, DefaultSize.Height, specified);
512 base.SetBoundsCore (x, y, width, height, specified);
515 // need to set the rectangles for all the support internal rects
516 // this is done here as a optimisation since this is an array of rects
517 if ((specified & BoundsSpecified.X) == BoundsSpecified.X ||
518 (specified & BoundsSpecified.Y) == BoundsSpecified.Y) {
519 // TODO set up all the datepart rects
523 // not sure why we're overriding this
524 protected override void WndProc (ref Message m) {
525 base.WndProc (ref m);
528 #endregion // protected methods
530 #region internal / private properties
532 // this is the region that the date and the check box is drawn on
533 internal Rectangle date_area_rect {
535 Rectangle rect = this.ClientRectangle;
537 // set the space to the left of the up/down button
538 if (rect.Width > (up_down_width + 4)) {
539 rect.Width -= (up_down_width + 4);
544 // set the space to the left of the up/down button
545 // TODO make this use up down button
546 if (rect.Width > (SystemInformation.VerticalScrollBarWidth + 4)) {
547 rect.Width -= SystemInformation.VerticalScrollBarWidth;
553 rect.Inflate (-2, -2);
558 // the rectangle for the drop down arrow
559 internal Rectangle drop_down_arrow_rect {
561 Rectangle rect = this.ClientRectangle;
562 rect.X = rect.Right - SystemInformation.VerticalScrollBarWidth - 2;
563 if (rect.Width > (SystemInformation.VerticalScrollBarWidth + 2)) {
564 rect.Width = SystemInformation.VerticalScrollBarWidth;
566 rect.Width = Math.Max (rect.Width - 2, 0);
569 rect.Inflate (0, -2);
574 // the part of the date that is currently hilighted
575 internal Rectangle hilight_date_area {
577 // TODO: put hilighted part calculation in here
578 return Rectangle.Empty;
584 #region internal / private methods
586 [MonoTODO("Fix Dropdown location when System.Windows.Forms.Screen gets added")]
587 private Point CalculateDropDownLocation (Rectangle parent_control_rect, Size child_size, bool align_left)
589 // default bottom left
590 Point location = new Point(parent_control_rect.Left + 5, parent_control_rect.Bottom);
591 // now adjust the alignment
593 location.X = parent_control_rect.Right - child_size.Width;
596 Point screen_location = PointToScreen (location);
597 // TODO: enable this part when screen comes into the classes
599 Rectangle working_area = Screen.FromControl(this).WorkingArea;
600 // now adjust if off the right side of the screen
601 if (screen_location.X < working_area.X) {
602 screen_location.X = working_area.X;
604 // now adjust if it should be displayed above control
605 if (screen_location.Y + child_size.Height > working_area.Bottom) {
606 screen_location.Y -= (parent_control_rect.Height + child_size.Height);
609 return screen_location;
612 // actually draw this control
613 internal void Draw (Rectangle clip_rect, Graphics dc)
615 ThemeEngine.Current.DrawDateTimePicker (dc, clip_rect, this);
618 // drop the calendar down
619 internal void DropDownMonthCalendar ()
621 // ensure the right date is set for the month_calendar
622 month_calendar.SetDate (this.date_value);
623 // get a rectangle that has the dimensions of the text area,
624 // but the height of the dtp control.
625 Rectangle align_area = this.date_area_rect;
626 align_area.Y = this.ClientRectangle.Y;
627 align_area.Height = this.ClientRectangle.Height;
629 // establish the month calendar's location
630 month_calendar.Location = CalculateDropDownLocation (
633 (this.DropDownAlign == LeftRightAlignment.Left));
634 month_calendar.Show ();
635 month_calendar.Focus ();
636 month_calendar.Capture = true;
638 // fire any registered events
639 if (this.DropDown != null) {
640 this.DropDown (this, EventArgs.Empty);
644 // hide the month calendar
645 internal void HideMonthCalendar ()
647 this.is_drop_down_visible = false;
648 Invalidate (drop_down_arrow_rect);
649 month_calendar.Capture = false;
650 if (month_calendar.Visible) {
651 month_calendar.Hide ();
655 // raised by any key down events
656 private void KeyPressHandler (object sender, KeyPressEventArgs e) {
664 // // if we lose focus and the drop down is up, then close it
665 // private void LostFocusHandler (object sender, EventArgs e)
667 // if (is_drop_down_visible && !month_calendar.Focused) {
668 // this.HideMonthCalendar ();
672 // fired when a user clicks on the month calendar to select a date
673 private void MonthCalendarDateSelectedHandler (object sender, DateRangeEventArgs e)
675 this.Value = e.Start.Date.Add (this.Value.TimeOfDay);
676 this.HideMonthCalendar ();
680 // to check if the mouse has come down on this control
681 private void MouseDownHandler (object sender, MouseEventArgs e)
685 // TODO: Process clicking for UPDown
687 if (is_drop_down_visible == false && drop_down_arrow_rect.Contains (e.X, e.Y)) {
688 is_drop_down_visible = true;
689 Invalidate (drop_down_arrow_rect);
690 DropDownMonthCalendar ();
692 // mouse down on this control anywhere else collapses it
693 if (is_drop_down_visible) {
694 HideMonthCalendar ();
701 // paint this control now
702 private void PaintHandler (object sender, PaintEventArgs pe) {
703 if (Width <= 0 || Height <= 0 || Visible == false)
706 Draw (pe.ClipRectangle, pe.Graphics);
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.ToLongTimeString ();
723 ret_value = date_value.ToLongDateString ();