可空与可绑定数据源的日期编辑选择控件 TDateEditPicker

(原创文章,转载请注明来源:http://blog.csdn.net/hulihui/article/details/5373252)  

 

项目中经常要碰到日期输入,.NET 2.0提供了TDateTimePicker存在如下方面的不足:

  • 数据源为空时,绑定的日期不匹配。
  • 不能设定日期文本框的颜色。

网上找到的却不能满足数据源绑定的基本要求。

 

1、基本思路

构思定制日期编选控件时,笔者尝试过如下两个思路:

  • 定制MonthCanlendar + 定制TextBox + 定制Button。实际使用时发现,定制MonthCalendar在年份往前选择时存在严重显示上的Bug,并且,在点击非控件区域时不能自动CloseUp。
  • 定制DateTimePicker + 定制TextBox。该思路最后证明是成功的。

2、定制文本框控件TDateEditBox

由于笔者以前编写过TNumEditBox,对TextBox有一定的了解,如实直接从TextBox上定制了一个输入日期的控件TDateEditBox。实现的关键技术如下:

  • 既然是输入日期,就只允许键盘,必须屏蔽掉上下文菜单。在构造函数中 this.ContextMenu = new ContextMenu()。
  • 捕获OnKeyDown事件,处理方向键、数字键、BackSpace、Delete。
  • 值需要区分Null、Valid情况。如果有值修改,则需要通知容器控件,满足绑定数据源更新的需求。

3、定制日期选择控件TDatePicker

从DateTimePicker继承,关键技术如下:

  • 如何捕获四个输入操作:鼠标点击确认日期、键盘Enter确认日期、ESC放弃选择、鼠标点控件外时自动Close。
  • 跟踪WndProc消息发现,鼠标点控件外时发生消息 WM_CAPTURCHANGED,此时表示放弃选择。
  • 跟踪WndProc消息发现,Enter、ESC和鼠标点选日期,均产生WM_IME_SETCONTENT消息。于是在ProProcessMessage事件中捕获ESC、Enter消息,在WndProc捕获鼠标选择日期消息。
  • ESC消息与鼠标点选日期相似,既有SetContent,从而确认,后有键盘消息,然后放弃。从而使得控件产生一次确认选择、然后放弃的消息。

4、定制UserControl

由前面两个定制控件组成,基本思路如下:

  • TDatePicker放在TDateEditBox下,但露出其Button部分,是的下拉框左边对齐。
  • 实现INotifyPropertyChanged,满足数据绑定源绑定与刷新要求。实际测试表明,该控件如果设置绑定更新方式为默认,则编辑框与直接给控件赋值时的刷新行为不一致。于是,固定为两种方式:Never、OnPropertyChanged。

有关代码的实现细节,请参考如下全部代码。

 

[c-sharp] view plain copy print ?
  1. using System;  
  2. using System.Windows.Forms;  
  3. using System.ComponentModel;  
  4. using System.Drawing;  
  5. using System.Text.RegularExpressions;  
  6.   
  7. namespace CSUST.Data  
  8. {  
  9.     [ToolboxItem(false)]  
  10.     public class TDateEditBox : TextBox  
  11.     {  
  12.         private const int MaxTextLength = 10;    // 固定10个字符   
  13.   
  14.         private char[] textChars = new char[] { ' '' '' '' ''-'' '' ''-'' '' ' };  
  15.         private char[] lastInvidTextChars = new char[MaxTextLength];  
  16.   
  17.         private char dateSeperator = '-';  // 分隔   
  18.   
  19.         private int minDateYear = 1950;  
  20.         private int maxDateYear = 2060;  
  21.   
  22.         private bool isNull = true;  
  23.         private bool isValid = false;  
  24.   
  25.         private Color normalDateForeColor;  
  26.         private Color invalidDateForeColor = Color.Red;  
  27.   
  28.         private DateTime date;  
  29.   
  30.         public event EventHandler DateChanged;  
  31.   
  32.         public TDateEditBox()  
  33.         {  
  34.             base.MaxLength = MaxTextLength;  
  35.             this.ContextMenu = new ContextMenu();  
  36.             normalDateForeColor = base.ForeColor;  
  37.             this.SetToNull();  
  38.         }  
  39.   
  40.         public new int MaxLength  
  41.         {  
  42.             get  
  43.             {  
  44.                 return base.MaxLength;  
  45.             }  
  46.             set  
  47.             {  
  48.                 base.MaxLength = MaxTextLength;  
  49.             }  
  50.         }  
  51.   
  52.         public int MinDateYear  
  53.         {  
  54.             get { return minDateYear; }  
  55.             set { minDateYear = value; }  
  56.         }  
  57.   
  58.         public int MaxDateYear  
  59.         {  
  60.             get { return maxDateYear; }  
  61.             set { maxDateYear = value; }  
  62.         }  
  63.   
  64.         public Color InvalidDateForeColor  
  65.         {  
  66.             get { return invalidDateForeColor; }  
  67.             set   
  68.             {   
  69.                 invalidDateForeColor = value;  
  70.                 if (isValid == false && isNull == false)  
  71.                 {  
  72.                     this.ShowWarnColor();  
  73.                 }  
  74.             }  
  75.         }  
  76.   
  77.         public override Color ForeColor  
  78.         {  
  79.             get { return base.ForeColor; }  
  80.             set  
  81.             {  
  82.                 if (value != invalidDateForeColor)  
  83.                 {  
  84.                     base.ForeColor = value;  
  85.                     normalDateForeColor = value;  
  86.                 }  
  87.                 if (isNull == true || isValid == true)  
  88.                 {  
  89.                     this.ShowNormalColor();  
  90.                 }  
  91.             }  
  92.         }  
  93.   
  94.         public char DateSeperator  
  95.         {  
  96.             get { return dateSeperator; }  
  97.             set  
  98.             {  
  99.                 if (value != '-' && value != '/' && value != '.')  
  100.                 {  
  101.                     dateSeperator = '-';  
  102.                 }  
  103.                 else  
  104.                 {  
  105.                     dateSeperator = value;  
  106.                 }  
  107.   
  108.                 textChars[4] = dateSeperator;  
  109.                 textChars[7] = dateSeperator;  
  110.   
  111.                 this.ShowText();  
  112.                 base.SelectionStart = 0;  
  113.             }  
  114.         }  
  115.   
  116.         public bool IsNull  
  117.         {  
  118.             get { return isNull; }  
  119.         }  
  120.   
  121.         public bool IsValid  
  122.         {  
  123.             get { return isValid; }  
  124.         }  
  125.   
  126.         public object Date  
  127.         {  
  128.             get  
  129.             {  
  130.                 if (isNull == true || isValid == false)  
  131.                 {  
  132.                     return null;  
  133.                 }  
  134.                 return date;  
  135.             }  
  136.             set  
  137.             {  
  138.                 if (value == null || value == DBNull.Value)  
  139.                 {  
  140.                     this.SetToNull();  
  141.                 }  
  142.                 else  
  143.                 {  
  144.                     date = (DateTime)value;  
  145.                     this.SetToDate(date);  
  146.                 }  
  147.   
  148.                 if (base.ForeColor != normalDateForeColor)  
  149.                 {  
  150.                     base.ForeColor = normalDateForeColor;  
  151.                 }  
  152.                 this.Invalidate();  
  153.             }  
  154.         }  
  155.   
  156.         public void OnDateChanged()  
  157.         {  
  158.             if (isValid == true || isNull == true)  
  159.             {  
  160.                 if (this.DateChanged != null)  
  161.                 {  
  162.                     this.DateChanged(this, EventArgs.Empty);  
  163.                 }  
  164.             }  
  165.         }  
  166.   
  167.         protected override void OnKeyDown(KeyEventArgs e)  
  168.         {  
  169.             if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift && (e.KeyCode == Keys.Right) || e.KeyCode == Keys.Left || e.KeyCode == Keys.Home || e.KeyCode == Keys.End)  
  170.             {  
  171.                 base.OnKeyDown(e);  
  172.                 return;  
  173.             }  
  174.   
  175.             if (e.KeyData == Keys.Tab || e.KeyData == Keys.Home || e.KeyData == Keys.End)  
  176.             {  
  177.                 base.OnKeyDown(e);  
  178.                 return;  
  179.             }  
  180.   
  181.             if (e.KeyCode == Keys.Back)  
  182.             {  
  183.                 this.BackSpace();  
  184.             }  
  185.             else if (e.KeyCode == Keys.Delete)  
  186.             {  
  187.                 this.Delete();  
  188.             }  
  189.             else if (e.KeyData == Keys.Left)  
  190.             {  
  191.                 this.MoveLeft();  
  192.             }  
  193.             else if (e.KeyData == Keys.Right)  
  194.             {  
  195.                 this.MoveRight();  
  196.             }  
  197.             else if ((e.KeyValue >= '0' && e.KeyValue <= '9') || e.KeyValue == ' ')  
  198.             {  
  199.                 this.InputDigit(e.KeyValue);  
  200.             }  
  201.             else if ((e.KeyCode >= Keys.NumPad0 && e.KeyCode <= Keys.NumPad9))  
  202.             {  
  203.                 int keyValue = (int)e.KeyCode - (int)Keys.NumPad0 + (int)Keys.D0;  
  204.                 this.InputDigit(keyValue);  
  205.             }  
  206.   
  207.             e.SuppressKeyPress = true;  
  208.             e.Handled = true;  
  209.   
  210.             this.ParseDateText();  
  211.         }  
  212.   
  213.         protected override void OnLeave(EventArgs e)  
  214.         {  
  215.             this.NormalizeDateText();  
  216.             base.OnLeave(e);  
  217.         }  
  218.   
  219.         protected override void OnGotFocus(EventArgs e)  
  220.         {  
  221.             base.OnGotFocus(e);  
  222.             base.SelectionLength = 0;  
  223.         }  
  224.   
  225.         private void SetToNull()  
  226.         {  
  227.             textChars[0] = ' ';  
  228.             textChars[1] = ' ';  
  229.             textChars[2] = ' ';  
  230.             textChars[3] = ' ';  
  231.             textChars[4] = dateSeperator;  
  232.             textChars[5] = ' ';  
  233.             textChars[6] = ' ';  
  234.             textChars[7] = dateSeperator;  
  235.             textChars[8] = ' ';  
  236.             textChars[9] = ' ';  
  237.   
  238.             isNull = true;  
  239.             isValid = false;  
  240.   
  241.             this.ShowText();  
  242.             base.SelectionStart = 0;  
  243.         }  
  244.   
  245.         private void SetToDate(DateTime date)  
  246.         {  
  247.             string today = date.ToString("yyyy-MM-dd");  
  248.             for (int k = 0; k < today.Length; k++)  
  249.             {  
  250.                 if (k != 4 && k != 7)  
  251.                 {  
  252.                     textChars[k] = today[k];  
  253.                 }  
  254.             }  
  255.   
  256.             isNull = false;  
  257.             isValid = true;  
  258.   
  259.             this.ShowText();  
  260.             base.SelectionStart = 0;  
  261.         }  
  262.   
  263.         private void SaveLastInvlidTextChars()  
  264.         {  
  265.             if (isNull == true)  
  266.             {  
  267.                 return;  
  268.             }  
  269.   
  270.             for (int k = 0; k < textChars.Length; k++)  
  271.             {  
  272.                 lastInvidTextChars[k] = textChars[k];  
  273.             }  
  274.         }  
  275.   
  276.         private void ParseDateText()  
  277.         {  
  278.             string y = new string(textChars, 0, 4);  
  279.             string m = new string(textChars, 5, 2);  
  280.             string d = new string(textChars, 8, 2);  
  281.   
  282.             string yy = y.Trim();  
  283.             string mm = m.Trim();  
  284.             string dd = d.Trim();  
  285.   
  286.             if (string.IsNullOrEmpty(yy) == true && string.IsNullOrEmpty(mm) == true && string.IsNullOrEmpty(dd) == true)  
  287.             {  
  288.                 bool preIsNull = isNull;  
  289.   
  290.                 isNull = true;  
  291.                 isValid = false;  
  292.   
  293.                 if (preIsNull != isNull)  
  294.                 {  
  295.                     this.ShowNormalColor();  
  296.                     this.OnDateChanged();  
  297.                 }  
  298.   
  299.                 return;  
  300.             }  
  301.   
  302.             isNull = false;  
  303.   
  304.             if (string.IsNullOrEmpty(yy) == true || string.IsNullOrEmpty(mm) == true || string.IsNullOrEmpty(dd) == true)  
  305.             {  
  306.                 isValid = false;  
  307.                 this.SaveLastInvlidTextChars();  
  308.                 this.ShowWarnColor();  
  309.                 return;  
  310.             }  
  311.   
  312.             if (Regex.IsMatch(yy, @"/d{4}") == false || Regex.IsMatch(mm, @"/d{1,2}") == false || Regex.IsMatch(dd, @"/d{1,2}") == false)  
  313.             {  
  314.                 isValid = false;  
  315.                 this.SaveLastInvlidTextChars();  
  316.                 this.ShowWarnColor();  
  317.                 return;  
  318.             }  
  319.   
  320.             int year = int.Parse(yy);  
  321.             int month = int.Parse(mm);  
  322.             int day = int.Parse(dd);  
  323.   
  324.             if (year < minDateYear || year > maxDateYear || month < 1 || month > 12 || day < 1 || day > DateTime.DaysInMonth(year, month))  
  325.             {  
  326.                 isValid = false;  
  327.                 this.SaveLastInvlidTextChars();  
  328.                 this.ShowWarnColor();  
  329.                 return;  
  330.             }  
  331.   
  332.             isValid = true;  
  333.             this.ShowNormalColor();  
  334.   
  335.             bool modified = false;  
  336.             if (year != date.Year || month != date.Month || day != date.Day)  
  337.             {  
  338.                 modified = true;  
  339.             }  
  340.               
  341.             date = new DateTime(year, month, day);  
  342.   
  343.             if (modified == true)  
  344.             {  
  345.                 this.OnDateChanged();  
  346.             }  
  347.         }  
  348.   
  349.         private void NormalizeDateText()  
  350.         {  
  351.             if (isValid == false || isNull == true)  
  352.             {  
  353.                 return;  
  354.             }  
  355.   
  356.             if (textChars[5] == ' ' || textChars[6] == ' ' || textChars[8] == ' ' || textChars[9] == ' ')  
  357.             {  
  358.                 if (textChars[5] == ' ')  
  359.                 {  
  360.                     textChars[5] = '0';  
  361.                 }  
  362.   
  363.                 if (textChars[6] == ' ')  
  364.                 {  
  365.                     textChars[6] = textChars[5];  
  366.                     textChars[5] = '0';  
  367.                 }  
  368.   
  369.                 if (textChars[8] == ' ')  
  370.                 {  
  371.                     textChars[8] = '0';  
  372.                 }  
  373.   
  374.                 if (textChars[9] == ' ')  
  375.                 {  
  376.                     textChars[9] = textChars[8];  
  377.                     textChars[8] = '0';  
  378.                 }  
  379.   
  380.                 ShowText();  
  381.             }  
  382.         }  
  383.   
  384.         private void ShowText()  
  385.         {  
  386.             if (isNull == false && isValid == false)  
  387.             {  
  388.                 if (base.ForeColor != invalidDateForeColor)  
  389.                 {  
  390.                     base.ForeColor = invalidDateForeColor;  
  391.                 }  
  392.             }  
  393.             else  
  394.             {  
  395.                 if (base.ForeColor != normalDateForeColor)  
  396.                 {  
  397.                     base.ForeColor = normalDateForeColor;  
  398.                 }  
  399.             }  
  400.   
  401.             base.Text = new string(textChars);  
  402.         }  
  403.   
  404.         private void ShowNormalColor()  
  405.         {  
  406.             if (base.ForeColor != normalDateForeColor)  
  407.             {  
  408.                 base.ForeColor = normalDateForeColor;  
  409.             }  
  410.             this.Invalidate();  
  411.         }  
  412.   
  413.         private void ShowWarnColor()  
  414.         {  
  415.             if (base.ForeColor != invalidDateForeColor)  
  416.             {  
  417.                 base.ForeColor = invalidDateForeColor;  
  418.             }  
  419.             this.Invalidate();  
  420.         }  
  421.   
  422.         public void ResumeLastInvalidText()  
  423.         {  
  424.             for (int k = 0; k < textChars.Length; k++)  
  425.             {  
  426.                 textChars[k] = lastInvidTextChars[k];  
  427.             }  
  428.   
  429.             if (base.ForeColor != invalidDateForeColor)  
  430.             {  
  431.                 base.ForeColor = invalidDateForeColor;  
  432.             }  
  433.   
  434.             isValid = false;  
  435.             base.Text = new string(textChars);  
  436.             this.OnDateChanged();  
  437.         }  
  438.   
  439.         private void Delete()  
  440.         {  
  441.             if (base.SelectionLength <= 1)  
  442.             {  
  443.                 this.Delete(base.SelectionStart);  
  444.             }  
  445.             else  
  446.             {  
  447.                 int start = base.SelectionStart + base.SelectionLength;  
  448.                 int end = base.SelectionStart + 1;  
  449.                 for (int k = start; k >= end; k--)  
  450.                 {  
  451.                     BackSpace(k);  
  452.                 }  
  453.             }  
  454.         }  
  455.   
  456.         private void Delete(int selectionStart)  
  457.         {  
  458.             if (AtTextEnd(selectionStart) == true)  
  459.             {  
  460.                 return;  
  461.             }  
  462.   
  463.             this.BackSpace(selectionStart + 1);  
  464.         }  
  465.   
  466.         private void BackSpace()  
  467.         {  
  468.             this.BackSpace(base.SelectionStart);  
  469.         }  
  470.   
  471.         private void BackSpace(int selectionStart)  
  472.         {  
  473.             int curPos = selectionStart;  
  474.   
  475.             if (curPos == 0)  
  476.             {  
  477.                 return;  
  478.             }  
  479.   
  480.             if (AtSeperatorRight(curPos) == true)  
  481.             {  
  482.                 base.SelectionStart = curPos - 1;  
  483.                 return;  
  484.             }  
  485.   
  486.             if (AtTextEnd(curPos) == true)  
  487.             {  
  488.                 textChars[textChars.Length - 1] = ' ';  
  489.                 ShowText();  
  490.                 base.SelectionStart = curPos - 1;  
  491.                 return;  
  492.             }  
  493.   
  494.             if (AtSeperatorLeft(curPos) == true)  
  495.             {  
  496.                 textChars[curPos - 1] = ' ';  
  497.                 ShowText();  
  498.                 base.SelectionStart = curPos - 1;  
  499.                 return;  
  500.             }  
  501.   
  502.             int k = curPos;  
  503.             while (AtSeperatorLeft(k) == false && AtTextEnd(k) == false)  
  504.             {  
  505.                 textChars[k - 1] = textChars[k];  
  506.                 k++;  
  507.             }  
  508.             textChars[k - 1] = ' ';  
  509.   
  510.             ShowText();  
  511.             base.SelectionStart = curPos - 1;  
  512.         }  
  513.   
  514.         private void InputDigit(int keyValue)  
  515.         {  
  516.             if (AtTextEnd() == true)  
  517.             {  
  518.                 return;  
  519.             }  
  520.   
  521.             int curPos = base.SelectionStart;  
  522.             int newPos = curPos;  
  523.   
  524.             if (AtSeperatorLeft() == true)  
  525.             {  
  526.                 textChars[base.SelectionStart + 1] = (char)keyValue;  
  527.                 newPos = curPos + 2;  
  528.             }  
  529.             else if (AtSeperatorLeft(curPos + 1) == true)  
  530.             {  
  531.                 textChars[base.SelectionStart] = (char)keyValue;  
  532.                 newPos = curPos + 2;  
  533.             }  
  534.             else  
  535.             {  
  536.                 textChars[base.SelectionStart] = (char)keyValue;  
  537.   
  538.                 if (AtSeperatorRight(curPos + 1) == true)  
  539.                 {  
  540.                     curPos++;  
  541.                 }  
  542.                 newPos = curPos + 1;  
  543.             }  
  544.   
  545.             this.ShowText();  
  546.             base.SelectionStart = newPos;  
  547.         }  
  548.   
  549.         private void MoveLeft()  
  550.         {  
  551.             if (base.SelectionStart == 0)  
  552.             {  
  553.                 return;  
  554.             }  
  555.   
  556.             if (AtSeperatorRight(base.SelectionStart) == true)  
  557.             {  
  558.                 base.SelectionStart -= 2;  
  559.             }  
  560.             else  
  561.             {  
  562.                 base.SelectionStart -= 1;  
  563.             }  
  564.         }  
  565.   
  566.         private void MoveRight()  
  567.         {  
  568.             if (this.AtTextEnd() == true)  
  569.             {  
  570.                 return;  
  571.             }  
  572.   
  573.             if (AtSeperatorLeft(base.SelectionStart + 1) == true)  
  574.             {  
  575.                 base.SelectionStart += 2;  
  576.             }  
  577.             else  
  578.             {  
  579.                 base.SelectionStart += 1;  
  580.             }  
  581.         }  
  582.   
  583.         private bool AtSeperatorLeft(int curPos)  
  584.         {  
  585.             if (curPos == 4 || curPos == 7)  
  586.             {  
  587.                 return true;  
  588.             }  
  589.             return false;  
  590.         }  
  591.   
  592.         private bool AtSeperatorLeft()  
  593.         {  
  594.             return AtSeperatorLeft(base.SelectionStart);  
  595.         }  
  596.   
  597.         private bool AtSeperatorRight(int curPos)  
  598.         {  
  599.             if (curPos == 5 || curPos == 8)  
  600.             {  
  601.                 return true;  
  602.             }  
  603.             return false;  
  604.         }  
  605.   
  606.         private bool AtSeperatorRight()  
  607.         {  
  608.             return AtSeperatorRight(base.SelectionStart);  
  609.         }  
  610.   
  611.         private bool AtTextEnd(int curPos)  
  612.         {  
  613.             if (curPos == textChars.Length)  
  614.             {  
  615.                 return true;  
  616.             }  
  617.             return false;  
  618.         }  
  619.   
  620.         private bool AtTextEnd()  
  621.         {  
  622.             return AtTextEnd(base.SelectionStart);  
  623.         }  
  624.     }  
  625.   
  626.     [ToolboxItem(false)]  
  627.     public class TDatePicker : DateTimePicker  
  628.     {  
  629.         private bool isDropdown = false;  
  630.   
  631.         private DateTime dateBeforeDropDown;  
  632.   
  633.         private const int WM_IME_SETCONTENT = 0x0281;  
  634.         private const int WM_CAPTURECHANGED = 0x0215;  
  635.         private const int WM_KEY_DOWN = 0x100;  
  636.         private const int WM_KEY_UP = 0x101;  
  637.         private const int WM_KEY_ENTER = 0x000d;  
  638.         private const int WM_KEY_ESC = 0x001b;  
  639.   
  640.         private bool msgHandledAfterCloseup = true;  
  641.         protected bool msgHandledByImeSetContent= false;  
  642.   
  643.         public event EventHandler DateConfirmed;  
  644.         public event EventHandler DateAbandoned;  
  645.   
  646.         protected override void OnDropDown(EventArgs eventargs)  
  647.         {  
  648.             msgHandledAfterCloseup = true;  
  649.             isDropdown = true;  
  650.   
  651.             dateBeforeDropDown = this.Value.Date;  
  652.             base.OnDropDown(eventargs);  
  653.         }  
  654.   
  655.         protected override void OnCloseUp(EventArgs eventargs)  
  656.         {  
  657.             isDropdown = false;  
  658.   
  659.             msgHandledAfterCloseup = false;  
  660.             msgHandledByImeSetContent = false;  
  661.   
  662.             base.OnCloseUp(eventargs);  
  663.         }  
  664.   
  665.         protected override void OnKeyDown(KeyEventArgs e)  
  666.         {  
  667.             if (isDropdown == true && (e.KeyCode == Keys.Left || e.KeyCode == Keys.Right ||  
  668.                 e.KeyCode == Keys.Up || e.KeyCode == Keys.Down ||  
  669.                 e.KeyCode == Keys.Home || e.KeyCode == Keys.End ||  
  670.                 e.KeyCode == Keys.PageUp || e.KeyCode == Keys.PageDown))  
  671.             {  
  672.                 base.OnKeyDown(e);  // 不需要捕获 ESC/Enter 健   
  673.             }  
  674.             else  
  675.             {  
  676.                 e.SuppressKeyPress = true;  
  677.                 e.Handled = true;  
  678.             }  
  679.         }  
  680.   
  681.         private void OnDateConfirmed()  
  682.         {  
  683.             if (this.DateConfirmed != null)  
  684.             {  
  685.                 this.DateConfirmed(this, EventArgs.Empty);  
  686.             }  
  687.         }  
  688.   
  689.         private void OnDateAbandoned()  
  690.         {  
  691.             if (this.Value.Date != dateBeforeDropDown)  
  692.             {  
  693.                 this.Value = dateBeforeDropDown;  
  694.             }  
  695.   
  696.             if (this.DateConfirmed != null)  
  697.             {  
  698.                 this.DateAbandoned(this, EventArgs.Empty);  
  699.             }  
  700.         }  
  701.   
  702.         public override bool PreProcessMessage(ref Message msg)  
  703.         {  
  704.             if (msgHandledAfterCloseup == false && msg.Msg == WM_KEY_UP && msg.WParam.ToInt32() ==WM_KEY_ENTER)  
  705.             {  
  706.                 msgHandledAfterCloseup = true;  
  707.                 this.OnDateConfirmed();  
  708.             }  
  709.             if ((msgHandledAfterCloseup == false || msgHandledByImeSetContent == true) && (msg.Msg == WM_KEY_UP && msg.WParam.ToInt32() == WM_KEY_ESC))  
  710.             {  
  711.                 msgHandledByImeSetContent = false;  
  712.                 msgHandledAfterCloseup = true;  
  713.                 this.OnDateAbandoned();  
  714.             }  
  715.             return base.PreProcessMessage(ref msg);  
  716.         }  
  717.   
  718.         protected override void WndProc(ref Message m)  
  719.         {  
  720.             if (m.Msg == WM_CAPTURECHANGED && msgHandledAfterCloseup == false)  
  721.             {  
  722.                 msgHandledAfterCloseup = true;  
  723.                 msgHandledByImeSetContent = false;                 
  724.                 this.OnDateAbandoned();  
  725.             }  
  726.             else if (m.Msg == WM_IME_SETCONTENT && msgHandledAfterCloseup == false)  
  727.             {  
  728.                 msgHandledAfterCloseup = true;  
  729.                 msgHandledByImeSetContent = true;  
  730.                 this.OnDateConfirmed();  
  731.             }  
  732.   
  733.             if (m.Msg == WM_KEY_DOWN)  // keydown   
  734.             {  
  735.                 msgHandledByImeSetContent = false;  
  736.             }  
  737.   
  738.             base.WndProc(ref m);  
  739.         }  
  740.     }  
  741.   
  742.     public class TDateEditPicker : UserControl, INotifyPropertyChanged  
  743.     {  
  744.         private IContainer components = null;  
  745.   
  746.         private const int MINDATEYEAR = 1949;  
  747.         private const int MAXDATEYEAR = 2100;  
  748.   
  749.         private TDateEditBox dateEditBox;  
  750.         private TDatePicker datePicker;  
  751.   
  752.         private bool isNullWhenDropDown;  
  753.         private bool isValidWhenDropDown;  
  754.         private string version = "1.2";  
  755.         private int nativeEditBoxWidth;  
  756.   
  757.         public event PropertyChangedEventHandler PropertyChanged;  
  758.   
  759.         public TDateEditPicker()  
  760.         {  
  761.             this.InitializeComponent();  
  762.   
  763.             this.DataBindings.DefaultDataSourceUpdateMode = DataSourceUpdateMode.Never;  // 强制默认方式   
  764.   
  765.             this.dateEditBox.Leave += new EventHandler(this.DateEditBox_Leave);  
  766.             this.datePicker.DropDown += new EventHandler(this.DateTimePicker_DropDown);  
  767.             this.dateEditBox.DateChanged += new EventHandler(this.DateEditBox_DateChanged);  
  768.             this.datePicker.CloseUp += new EventHandler(this.DateTimePicker_CloseUp);  
  769.             this.datePicker.DateConfirmed += new EventHandler(this.DateTimePicker_DataConfirmed);  
  770.             this.datePicker.DateAbandoned += new EventHandler(this.DateTimePicker_DateAbandoned);  
  771.         }  
  772.   
  773.         private void InitializeComponent()  
  774.         {  
  775.             this.dateEditBox = new TDateEditBox();  
  776.             this.datePicker = new TDatePicker();  
  777.   
  778.             this.SuspendLayout();  
  779.   
  780.             this.datePicker.Location = new Point(1, 0);  
  781.             this.datePicker.Name = "datePicker";  
  782.             this.datePicker.Value = DateTime.Now;  
  783.             this.datePicker.MinDate = new DateTime(this.dateEditBox.MinDateYear, 1, 1);  
  784.             this.datePicker.MaxDate = new DateTime(this.dateEditBox.MaxDateYear, 12, 31);  
  785.             this.datePicker.Format = DateTimePickerFormat.Custom;  
  786.             this.datePicker.CustomFormat = "          ";  
  787.   
  788.             this.dateEditBox.Location = new Point(0, 0);  
  789.             this.dateEditBox.Name = "dateEditBox";  
  790.             this.nativeEditBoxWidth = this.dateEditBox.Height;  
  791.   
  792.             this.Controls.Add(this.datePicker);  
  793.             this.Controls.Add(this.dateEditBox);  
  794.             this.Name = "TDateEditPicker";  
  795.             this.Size = new Size(this.datePicker.Width + 2, this.dateEditBox.Height + 2);  
  796.   
  797.             this.dateEditBox.BringToFront();  
  798.   
  799.             this.ResumeLayout();  
  800.         }  
  801.   
  802.         protected override void Dispose(bool disposing)  
  803.         {  
  804.             if (disposing == true)  
  805.             {  
  806.                 if (components != null)  
  807.                 {  
  808.                     components.Dispose();  
  809.                 }  
  810.             }  
  811.             base.Dispose(disposing);  
  812.         }  
  813.   
  814.         protected override void OnResize(EventArgs e)  
  815.         {  
  816.             base.OnResize(e);  
  817.             this.datePicker.Width = this.Width - 2;  
  818.             this.dateEditBox.Width = this.datePicker.Width - this.nativeEditBoxWidth + 2;  
  819.         }  
  820.   
  821.         protected override void OnForeColorChanged(EventArgs e)  
  822.         {  
  823.             base.OnForeColorChanged(e);  
  824.             dateEditBox.ForeColor = base.ForeColor;  
  825.         }  
  826.   
  827.         private void DateEditBox_Leave(object sender, EventArgs e)  
  828.         {  
  829.             if (this.IsValid == true && this.IsNull == false)  
  830.             {  
  831.                 this.datePicker.Value = (DateTime)dateEditBox.Date;  
  832.             }  
  833.             else  
  834.             {  
  835.                 this.datePicker.Value = DateTime.Now.Date;  
  836.             }  
  837.         }  
  838.   
  839.         private void DateEditBox_DateChanged(object sender, EventArgs e)  
  840.         {  
  841.             if (this.IsNull == true || this.IsValid == true)  
  842.             {  
  843.                 this.NotifyPropertyChanged("Date");  
  844.             }  
  845.         }  
  846.   
  847.         private void DateTimePicker_DropDown(object sender, EventArgs e)  
  848.         {  
  849.             isNullWhenDropDown = this.IsNull;  
  850.             isValidWhenDropDown = this.IsValid;  
  851.         }  
  852.   
  853.         private void DateTimePicker_DataConfirmed(object sender, EventArgs e)  
  854.         {  
  855.             this.dateEditBox.Date = this.datePicker.Value.Date;  
  856.             this.NotifyPropertyChanged("Date");  
  857.   
  858.         }  
  859.   
  860.         private void DateTimePicker_DateAbandoned(object sender, EventArgs e)  
  861.         {  
  862.             if (isNullWhenDropDown == true)  
  863.             {  
  864.                 this.dateEditBox.Date = null;  
  865.             }  
  866.             else if (isValidWhenDropDown == false)  
  867.             {  
  868.                 this.dateEditBox.ResumeLastInvalidText();  
  869.             }  
  870.         }  
  871.   
  872.         private void DateTimePicker_CloseUp(object sender, EventArgs e)  
  873.         {  
  874.         }  
  875.   
  876.         private void NotifyPropertyChanged(string info)  
  877.         {  
  878.             if (this.DataBindings != null && this.DataBindings.Count > 0)  
  879.             {  
  880.                 if (this.DataBindings[0].DataSourceUpdateMode == DataSourceUpdateMode.OnValidation)  // == never   
  881.                 {  
  882.                     return;  
  883.                 }  
  884.             }  
  885.   
  886.             if (this.PropertyChanged != null)  
  887.             {  
  888.                 this.PropertyChanged(thisnew PropertyChangedEventArgs(info));  
  889.             }  
  890.         }  
  891.   
  892.         [Category("Custom"), Browsable(true)]  
  893.         public string Version  
  894.         {  
  895.             get { return version; }  
  896.         }  
  897.   
  898.         [Category("Custom"), Browsable(true), DefaultValue('-')]  
  899.         [Description("Date seperator, only use three chars: .-/.")]  
  900.         public char DateSeperator  
  901.         {  
  902.             get { return dateEditBox.DateSeperator; }  
  903.             set { dateEditBox.DateSeperator = value; }  
  904.         }  
  905.   
  906.         [Category("Custom"),Browsable(true)]  
  907.         public int MinDateYear  
  908.         {  
  909.             get { return this.dateEditBox.MinDateYear; }  
  910.             set  
  911.             {  
  912.                 int tmpValue = value;  
  913.                 if (tmpValue < MINDATEYEAR || tmpValue > MAXDATEYEAR)  
  914.                 {  
  915.                     tmpValue = MINDATEYEAR;  
  916.                 }  
  917.                 if (this.MinDateYear != tmpValue)  
  918.                 {  
  919.                     this.dateEditBox.MinDateYear = tmpValue;  
  920.                     this.datePicker.MinDate = new DateTime(tmpValue, 1, 1);  
  921.                 }  
  922.             }  
  923.         }  
  924.   
  925.         [Category("Custom"), Browsable(true)]  
  926.         public int MaxDateYear  
  927.         {  
  928.             get { return this.dateEditBox.MaxDateYear; }  
  929.             set  
  930.             {  
  931.                 int tmpValue = value;  
  932.                 if (tmpValue < MINDATEYEAR || tmpValue > MAXDATEYEAR)  
  933.                 {  
  934.                     tmpValue = MAXDATEYEAR;  
  935.                 }  
  936.                 else  
  937.                 {  
  938.                     this.dateEditBox.MaxDateYear = tmpValue;  
  939.                     this.datePicker.MaxDate = new DateTime(tmpValue, 12, 31);  
  940.                 }  
  941.             }  
  942.         }  
  943.   
  944.         [Category("Custom")]  
  945.         [Description("Is the date is null.")]  
  946.         public bool IsNull  
  947.         {  
  948.             get { return dateEditBox.IsNull; }  
  949.         }  
  950.   
  951.         [Category("Custom")]  
  952.         [Description("Is the date is valid.")]  
  953.         public bool IsValid  
  954.         {  
  955.             get { return dateEditBox.IsValid; }  
  956.         }  
  957.   
  958.         [Bindable(true), Browsable(false)]  
  959.         public Object Date  
  960.         {  
  961.             get  
  962.             {  
  963.                 return this.dateEditBox.Date;  
  964.             }  
  965.             set  
  966.             {  
  967.                 this.dateEditBox.Date = value;  
  968.                 this.NotifyPropertyChanged("Date");  
  969.             }  
  970.         }  
  971.   
  972.         [Category("Custom")]  
  973.         [Description("The font color when date is invalid.")]  
  974.         [DefaultValue("Red"), Browsable(true)]  
  975.         public Color InvalidDateForeColor  
  976.         {  
  977.             get { return this.dateEditBox.InvalidDateForeColor; }  
  978.             set { this.dateEditBox.InvalidDateForeColor = value; }  
  979.         }  
  980.   
  981.     }  
  982. }  

 

 4、结语

TDateEditPicker控件还有一些属性,如:设置日期分隔符、设置前景颜色、设置无效日期颜色、最大最小日期等。具体使用时,拷贝上述控件代码为一个类文件,加到自己的Project中然后编译,此时在工具条上将看到该控件。拖拉到窗体上即可。

 

由于项目中需要用到不少日期处理,原先做过的一个NullableDateTimePicker不能满足要求。在参考网上的一些资料基础上,花了约5天计50多小时。时间仓促,测试不充分,有同行使用时如果发现Bug,请不吝告之,笔者将进一步完善。

 

升级历史

  1. 2010-03-13:TDateEditPicker控件及演示(Ver1.3)。修改了部分代码,规范了字段和属性命名,整理了多余代码。
  2. 2010-03-14:TDateEditPicker演示及源码(Ver1.5)。增加了日期显示格式和分隔符号,增加了设计期初始日期属性,优化了代码。
  3. 2010-03-16:TDateEditPicker控件及演示(Ver1.6)。解决了通过Tab移动到该控件时输入框不获得聚焦的Bug。
  4. 2010-03-18:TDateEditPicker控件及演示(Ver1.8)。解决了直接给日期后下拉不显示该日期的一个Bug, 下拉关闭后聚焦的Bug。
  5. 2010-04-08:Version1.9。在 TabControl 容器中的几个TabPage上,只有一个Page显示DatePicker控件,其他的Page只有DateEditBox控件。
  6. 2010-04-17:Version1.11。去掉了DataPickerVisible属性(该属性导致控件在TabControl的多页面上异常),增加了ValueChanged事件。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值