context: using System;
  using System.Windows.Forms;
  using System.Diagnostics;
  using System.Text;
  
  namespace Fireasy.Windows.Forms
  {
      #region DateTimeBehavior
      public class DateTimeBehavior : TimeBehavior
      {
          // Fields
          private DateBehavior m_dateBehavior;
  
          /// <summary>
          ///   Internal values that are added/removed to the <see cref="Behavior.Flags" /> property by other
          ///   properties of this class. </summary>
          [Flags]
          protected new enum Flag
          {
              /// <summary> Makes this object behave like the Date behavior, where only the date part is shown. </summary>
              /// <seealso cref="DateBehavior" />
              DateOnly = 0x00100000,
  
              /// <summary> Makes this object behave like the Time behavior, where only the time part is shown. </summary>
              /// <seealso cref="TimeBehavior" />
              TimeOnly = 0x00200000,
  
              /// <summary> The day is displayed in front of the month. </summary>
              /// <seealso cref="ShowDayBeforeMonth" />
              DayBeforeMonth = 0x00010000,
  
              /// <summary> The hour is shown in 24-hour format (00 to 23). </summary>
              /// <seealso cref="TimeBehavior.Show24HourFormat" />
              TwentyFourHourFormat = 0x00020000,
  
              /// <summary> The seconds are also shown. </summary>
              /// <seealso cref="TimeBehavior.ShowSeconds" />
              WithSeconds = 0x00040000
          };
  
          /// <summary>
          ///   Initializes a new instance of the DateTimeBehavior class by associating it with a TextBoxBase derived object. </summary>
          /// <param name="textBox">
          ///   The TextBoxBase object to associate with this behavior.  It must not be null. </param>
          /// <exception cref="ArgumentNullException">textBox is null. </exception>
          /// <remarks>
          ///   This constructor retrieves many of the properties from the user's system. </remarks>
          /// <seealso cref="System.Windows.Forms.TextBoxBase" />	
          public DateTimeBehavior(TextBoxBase textBox)
              :
              base(textBox)
          {
              m_dateBehavior = new DateBehavior(textBox, false);  // does not add the event handlers
              m_flags |= m_dateBehavior.Flags;
              m_hourStart = 11;
          }
  
          /// <summary>
          ///   Initializes a new instance of the DateTimeBehavior class by copying it from 
          ///   another DateTimeBehavior object. </summary>
          /// <param name="behavior">
          ///   The DateTimeBehavior object to copied (and then disposed of).  It must not be null. </param>
          /// <exception cref="ArgumentNullException">behavior is null. </exception>
          /// <remarks>
          ///   After the behavior.TextBox object is copied, Dispose is called on the behavior parameter. </remarks>
          public DateTimeBehavior(DateTimeBehavior behavior)
              :
              base(behavior)
          {
              m_dateBehavior = new DateBehavior(m_textBox, false);  // does not add the event handlers
          }
  
          /// <summary>
          ///   Sets the month, day, year, hour, minute, and second on the textbox. </summary>
          /// <param name="year">
          ///   The year to set. </param>
          /// <param name="month">
          ///   The month to set. </param>
          /// <param name="day">
          ///   The day to set. </param>
          /// <param name="hour">
          ///   The hour to set, between 0 and 23. </param>
          /// <param name="minute">
          ///   The minute to set, between 0 and 59. </param>
          /// <remarks>
          ///   This is a convenience method to set each value individually using a single method. 
          ///   A <see cref="DateTime" /> object is constructed using these parameters, so they must be valid. 
          ///   If the second is being shown on the textbox, it is set to 0.  </remarks>
          public void SetDateTime(int year, int month, int day, int hour, int minute)
          {
              SetDateTime(year, month, day, hour, minute, 0);
          }
  
          /// <summary>
          ///   Sets the month, day, year, hour, minute, and second on the textbox. </summary>
          /// <param name="year">
          ///   The year to set. </param>
          /// <param name="month">
          ///   The month to set. </param>
          /// <param name="day">
          ///   The day to set. </param>
          /// <param name="hour">
          ///   The hour to set, between 0 and 23. </param>
          /// <param name="minute">
          ///   The minute to set, between 0 and 59. </param>
          /// <param name="second">
          ///   The second to set, between 0 and 59. </param>
          /// <remarks>
          ///   This is a convenience method to set each value individually using a single method. 
          ///   A <see cref="DateTime" /> object is constructed using these parameters, so they must be valid. </remarks>
          public void SetDateTime(int year, int month, int day, int hour, int minute, int second)
          {
              if (HasFlag((int)Flag.DateOnly))
                  m_dateBehavior.SetDate(year, month, day);
              else if (HasFlag((int)Flag.TimeOnly))
                  SetTime(hour, minute, second);
              else
              {
                  Debug.Assert(m_dateBehavior.IsWithinRange(new DateTime(year, month, day)));
                  m_textBox.Text = m_dateBehavior.GetFormattedDate(year, month, day) + ' ' + GetFormattedTime(hour, minute, second, "");
              }
          }
  
          /// <summary>
          ///   Sets the month, day, and year on the textbox. </summary>
          /// <param name="year">
          ///   The year to set. </param>
          /// <param name="month">
          ///   The month to set. </param>
          /// <param name="day">
          ///   The day to set. </param>
          /// <remarks>
          ///   This is a convenience method to set each value individually using a single method. 
          ///   A <see cref="DateTime" /> object is constructed using these parameters, so they must be valid. </remarks>
          public void SetDate(int year, int month, int day)
          {
              if (HasFlag((int)Flag.DateOnly) || !HasFlag((int)Flag.TimeOnly))
                  m_dateBehavior.SetDate(year, month, day);
          }
  
          /// <summary>
          ///   Sets the hour, minute, and second on the textbox. </summary>
          /// <param name="hour">
          ///   The hour to set, between 0 and 23. </param>
          /// <param name="minute">
          ///   The minute to set, between 0 and 59. </param>
          /// <param name="second">
          ///   The second to set, between 0 and 59. </param>
          /// <remarks>
          ///   This is a convenience method to set each value individually using a single method. 
          ///   A <see cref="DateTime" /> object is constructed using these parameters, so they must be valid. </remarks>
          public new void SetTime(int hour, int minute, int second)
          {
              if (!HasFlag((int)Flag.DateOnly) && HasFlag((int)Flag.TimeOnly))
                  base.SetTime(hour, minute, second);
          }
  
          /// <summary>
          ///   Sets the hour and minute on the textbox. </summary>
          /// <param name="hour">
          ///   The hour to set, between 0 and 23. </param>
          /// <param name="minute">
          ///   The minute to set, between 0 and 59. </param>
          /// <remarks>
          ///   This is a convenience method to set each value individually using a single method. 
          ///   A <see cref="DateTime" /> object is constructed using these parameters, so they must be valid. 
          ///   If the second is being shown on the textbox, it is set to 0.  </remarks>
          public new void SetTime(int hour, int minute)
          {
              SetTime(hour, minute, 0);
          }
  
          /// <summary>
          ///   Gets or sets the month, day, year, hour, minute, and second on the textbox using a <see cref="DateTime" /> object. </summary>
          /// <remarks>
          ///   This property gets and sets the <see cref="DateTime" /> boxed inside an <c>object</c>.
          ///   This makes it flexible, so that if the textbox does not hold a valid date and time, a <c>null</c> is returned, 
          ///   instead of having to worry about an exception being thrown. </remarks>
          /// <example><code>
          ///   object obj = txtDateTime.Behavior.Value;
          ///   if (obj != null)
          ///   {
          ///     DateTime dtm = (DateTime)obj;
          ///     ...
          ///   } </code></example>
          /// <seealso cref="Month" />
          /// <seealso cref="Day" />
          /// <seealso cref="Year" />
          /// <seealso cref="TimeBehavior.Hour" />
          /// <seealso cref="TimeBehavior.Minute" />
          /// <seealso cref="TimeBehavior.Second" />
          public new object Value
          {
              get
              {
                  try
                  {
                      if (HasFlag((int)Flag.DateOnly))
                          return m_dateBehavior.Value;
                      if (HasFlag((int)Flag.TimeOnly))
                          return base.Value;
                      return new DateTime(Year, Month, Day, Hour, Minute, Second);
                  }
                  catch
                  {
                      return null;
                  }
              }
              set
              {
                  DateTime dt = (DateTime)value;
                  SetDateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second);
              }
          }
  
          /// <summary>
          ///   Gets or sets the month on the textbox. </summary>
          /// <exception cref="ArgumentOutOfRangeException">Setting this property with an invalid month. </exception>
          /// <remarks>
          ///   If the month is not being shown or is not valid on the textbox, this property will return 0.
          ///   This property must be set with a month that falls within the allowed range. </remarks>
          /// <seealso cref="Day" />
          /// <seealso cref="Year" />
          public int Month
          {
              get
              {
                  if (HasFlag((int)Flag.TimeOnly))
                      return 0;
                  return m_dateBehavior.Month;
              }
              set
              {
                  if (!HasFlag((int)Flag.TimeOnly))
                      m_dateBehavior.Month = value;
              }
          }
  
          /// <summary>
          ///   Gets or sets the day on the textbox. </summary>
          /// <exception cref="ArgumentOutOfRangeException">Setting this property with an invalid day. </exception>
          /// <remarks>
          ///   If the day is not being shown or is not valid on the textbox, this property will return 0.
          ///   This property must be set with a day that falls within the allowed range. </remarks>
          /// <seealso cref="Month" />
          /// <seealso cref="Year" />
          public int Day
          {
              get
              {
                  if (HasFlag((int)Flag.TimeOnly))
                      return 0;
                  return m_dateBehavior.Day;
              }
              set
              {
                  if (!HasFlag((int)Flag.TimeOnly))
                      m_dateBehavior.Day = value;
              }
          }
  
          /// <summary>
          ///   Gets or sets the year on the textbox. </summary>
          /// <exception cref="ArgumentOutOfRangeException">Setting this property with an invalid year. </exception>
          /// <remarks>
          ///   If the year is not being shown or is not valid on the textbox, this property will return 0.
          ///   This property must be set with a year that falls within the allowed range. </remarks>
          /// <seealso cref="Month" />
          /// <seealso cref="Day" />
          public int Year
          {
              get
              {
                  if (HasFlag((int)Flag.TimeOnly))
                      return 0;
                  return m_dateBehavior.Year;
              }
              set
              {
                  if (!HasFlag((int)Flag.TimeOnly))
                      m_dateBehavior.Year = value;
              }
          }
  
          /// <summary>
          ///   Checks if the textbox's date and/or time is valid and falls within the allowed range. </summary>
          /// <returns>
          ///   If the value is valid and falls within the allowed range, the return value is true; otherwise it is false. </returns>
          /// <seealso cref="RangeMin" />
          /// <seealso cref="RangeMax" />
          public override bool IsValid()
          {
              if (HasFlag((int)Flag.DateOnly))
                  return m_dateBehavior.IsValid();
              if (HasFlag((int)Flag.TimeOnly))
                  return base.IsValid();
              return (m_dateBehavior.IsValid() && base.IsValid());
          }
  
          /// <summary>
          ///   Gets or sets the minimum value allowed. </summary>
          /// <remarks>
          ///   By default, this property is set to DateTime(1900, 1, 1, 0, 0, 0).
          ///   The range is actively checked as the user enters the date but the time is only checked 
          ///   when the control loses focus, if one of the <see cref="ValidatingFlag" /> flags is set. </remarks>	
          /// <seealso cref="RangeMax" />
          public new DateTime RangeMin
          {
              get
              {
                  if (HasFlag((int)Flag.DateOnly))
                      return m_dateBehavior.RangeMin;
                  if (HasFlag((int)Flag.TimeOnly))
                      return base.RangeMin;
  
                  DateTime rangeMin = base.RangeMin;
                  return new DateTime(m_dateBehavior.RangeMin.Year, m_dateBehavior.RangeMin.Month, m_dateBehavior.RangeMin.Day, rangeMin.Hour, rangeMin.Minute, rangeMin.Second);
              }
              set
              {
                  base.RangeMin = value;
  
gt:                 if (HasFlag((int)Flag.DateOnly) || !HasFlag((int)Flag.TimeOnly))