C#Winform 可清空自定义日期控件

C#Winform自带的日期控件是不允许清空的, 网上虽有很多奇怪的做法,但实际上并不好用。下面是通过用户自定义控件, 来重新绘制的控件(DateBox);

支持:实时日期输入格式验证, 动画弹出下拉月历

控件分为两部分: 月历窗口(F_Calendar), 输入框(DateBox控件主体)

//F_Calendar.cs
using QueryNet.Base;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace QueryNet.Controls
{
    public partial class F_Calendar : Form
    {
     

 
        Timer _timer = null;
        DateBox _parent = null;

        public bool IsValid { get;protected set; }

        public bool IsFocused { get { return this.Focused || monthCalendar1.Focused; } }
        public F_Calendar(DateBox patent)
        {
            _parent = patent;
            if (_parent.FindForm() != null)
            {
                Form frmP = _parent.FindForm();
                if (!frmP.IsDisposed)
                {
                    frmP.LocationChanged += frmP_LocationChanged;
                }
            }
            _parent.LocationChanged += frmP_LocationChanged;


            InitializeComponent();
        }
  
        public Point ShowPosition { get; set; }

        public void refreshValue(Nullable<DateTime> value)
        {
            if (value.isEmpty())
            {
                monthCalendar1.SetDate(DateTime.Now);
            }
            else {
                monthCalendar1.SetDate(Convert.ToDateTime( value));
            }
        }
        private void m_today_Click(object sender, EventArgs e)
        {
            
        }

        private void F_Calendar_Load(object sender, EventArgs e)
        {
            this.StartPosition = FormStartPosition.Manual;
            this.LostFocus += new EventHandler(LostFocusEvent);
            monthCalendar1.LostFocus+= new EventHandler(LostFocusEvent);
            this.GotFocus += new EventHandler(GotFocusEvent);

            this.Visible = false;
            showForm(_parent);
        }

        private void frmP_LocationChanged(object sender, EventArgs e) {
            if (!_parent.isEmpty())
            {
                _parent.hideDropDown();
            }
        }

        private void F_Calendar_FormClosing(object sender, FormClosingEventArgs e)
        {
            CommonHelper.AnimateWindow(this.Handle, 200, CommonHelper.AW_BLEND | CommonHelper.AW_HIDE);
        }

        public void showForm(IWin32Window parent) {
            if (!ShowPosition.isEmpty()) { this.Location = ShowPosition; }
            // this.Visible = true;
            //this.Show(parent);
            //CommonHelper.ShowOwnedPopups(this.Handle, false);
            CommonHelper.AnimateWindow(this.Handle, 200, CommonHelper.AW_SLIDE | CommonHelper.AW_ACTIVE | CommonHelper.AW_VER_NEGATIVE);
            IsValid = true;
        }

        private void LostFocusEvent(object sender, EventArgs e)
        {
            //失去焦点要执行的代码
            if (!_parent.isEmpty())
            {
                if (!_parent.IsFocused && !this.Focused && !monthCalendar1.Focused )
                {
                    _parent.hideDropDown();
                    _parent.ReDraw();
                }
            }
        }

        private void GotFocusEvent(object sender, EventArgs e) {
            if (!_parent.isEmpty()) {
                _parent.ReDraw();
            }
        }

        private void timerTick(object sender, EventArgs e) {
            if (!_parent.isEmpty())
            {
                if (!_parent.IsFocused && !this.Focused && !monthCalendar1.Focused)
                {
                    _timer.Stop();
                    _timer.Dispose();
                    _timer = null;
                    _parent.hideDropDown();
                    _parent.ReDraw();
                }
            }
        }

        protected override void DestroyHandle()
        {
            if (!_timer.isEmpty()) {
                _timer.Dispose();
                _timer = null;
            }
            
            base.DestroyHandle();
        }


        private void monthCalendar1_DateSelected(object sender, DateRangeEventArgs e)
        {
            _parent.Value = e.Start;
            _parent.hideDropDown();
        }

       
    }
}

控件主体代码:

//DateBox.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using QueryNet.Base;
using System.Text.RegularExpressions;

namespace QueryNet.Controls
{


    public partial class DateBox : UserControl
    {

        protected bool _isDropDown = false;
        private bool _isMouseLeave = false;
        F_Calendar f_Calendar = null;
        private Nullable<DateTime> _value = null;
        private string _format = "YYYY-MM-DD";
        private string _maskFormat = "0000-00-00";
        private string _matchPattern = @"y{4,4}[\-\/]m{2,3}[\-\/]d{2,2}";
        Timer _validateTimer = null;


      


        /// <summary> 
        /// 边框颜色 
        /// </summary> 
        private Color _BorderColor = Color.FromArgb(0xA7, 0xA6, 0xAA);
        /// <summary> 
        /// 热点边框颜色 
        /// </summary> 
        private Color _HotColor = Color.FromArgb(0x33, 0x5E, 0xA8);

        private Color _TxtBackColor = SystemColors.Window;


        #region 属性 
        /// <summary> 
        /// 边框颜色 
        /// </summary> 
        [Category("外观"),
        Description("获得或设置控件的边框颜色"),
        DefaultValue(typeof(Color), "#A7A6AA")]
        public Color BorderColor
        {
            get
            {
                return this._BorderColor;
            }
            set
            {
                this._BorderColor = value;
                this.Invalidate();
            }
        }
        /// <summary> 
        /// 热点时边框颜色 
        /// </summary> 
        [Category("外观"),
        Description("获得或设置当鼠标经过控件时控件的边框颜色。只在控件的BorderStyle为FixedSingle时有效"),
        DefaultValue(typeof(Color), "#335EA8")]
        public Color HotColor
        {
            get
            {
                return this._HotColor;
            }
            set
            {
                this._HotColor = value;
                this.Invalidate();
            }
        }
        [Category("外观"),
       Description("输入框背景色")]
        public Color TxtBackColor {
            get { return _TxtBackColor; }
            set { _TxtBackColor = value; }
        }

        /// <summary>
        /// 值
        /// </summary>
        public Nullable<DateTime> Value
        {
            get { return _value; }
            set { setValue(value); }

        }
        /// <summary>
        /// 日期格式, 
        /// 数字格式:YYYY-MM-DD, MM-DD-YYYY,YYYY/MM/DD, MM/DD/YYYY
        /// 英文格式:YYYY-MMM-DD, MMM-DD-YYYY,YYYY/MM/DD, MMM/DD/YYYY
        /// </summary>
        public string Format
        {
            get { return _format; }
            set { setFormat(value); }

        }

        public bool IsFocused
        {
            get
            {
                return this.Focused || msk_input.Focused || p_dropdown.Focused || f_Calendar.Focused;
            }
        }
        #endregion 属性

        public DateBox()
        {
            InitializeComponent();
        }

        private void DateBox_Load(object sender, EventArgs e)
        {

            f_Calendar = new F_Calendar(this);
            this.GotFocus += new EventHandler(GetFocusEvent);
            this.LostFocus += new EventHandler(LostFocusEvent);
            msk_input.GotFocus += new EventHandler(GetFocusEvent);
            msk_input.LostFocus += new EventHandler(LostFocusEvent);
            p_dropdown.LostFocus += new EventHandler(LostFocusEvent);
            drawUI();
            _format = Format;
        }

        public void hideDropDown() {
            _isDropDown = false;
            f_Calendar.Hide();
        }

        protected void setValue(Nullable<DateTime> ao_value) {
            if (ao_value.isEmpty())
            {
                msk_input.Text = "";
                _value = null;
            }
            else {
                msk_input.Text =Convert.ToDateTime(ao_value).ToString(_format.Replace("Y", "y").Replace("D", "d").Replace("m", "M"));
            }
            validateInput();
        }

        private string getFormatMatch(string format) {
            /// 日期格式:YYYY-MM-DD, MM-DD-YYYY,YYYY/MM/DD, MM/DD/YYYY, 
            if (format.isEmpty()) { format = "YYYY-MM-DD"; }
            //YYYY-MM-DD
            format = format.ToUpper().Replace("/", "-");
            var pattern = @"y{4,4}[\-\/]m{1,2}[\-\/]d{1,2}";
            _matchPattern = pattern;
            var lo_match = Regex.Matches(format, pattern, RegexOptions.IgnoreCase);

            //MM-DD-YYYY
            if (lo_match.Count <= 0)
            {
                pattern = @"m{1,2}[\-\/]d{1,2}[\-\/]y{4,4}";
                _matchPattern = pattern;
                lo_match = Regex.Matches(format, pattern, RegexOptions.IgnoreCase);
            }
            //MM-DD
            if (lo_match.Count <= 0)
            {
                pattern = @"m{1,2}[\-\/]d{1,2}";
                _matchPattern = pattern;
                lo_match = Regex.Matches(format, pattern, RegexOptions.IgnoreCase);
            }
            //YYYY-MM
            if (lo_match.Count <= 0)
            {
                pattern = @"y{4,4}[\-\/]m{1,2}";
                _matchPattern = pattern;
                lo_match = Regex.Matches(format, pattern, RegexOptions.IgnoreCase);
            }
           
            if (lo_match.Count > 0)
            {
                return lo_match[0].Value;

            }
            else
            {
                return "YYYY-MM-DD";
            }
        }

    
        protected void setFormat(string format) {
            format = getFormatMatch(format);
            _maskFormat = format.Replace("Y", "0").Replace("M", "0").Replace("D", "0");
            _format = format;
            msk_input.Mask = _maskFormat;
        }


        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);
            switch (m.Msg) {
                case CommonHelper.WM_PAINT:
                    if (m.HWnd != this.Handle) { return; }
                    IntPtr hDC = CommonHelper.GetWindowDC(m.HWnd);
                    if (hDC.ToInt32() == 0) { return; }

                    using (System.Drawing.Graphics g = Graphics.FromHdc(hDC))
                    {
                        //只有在边框样式为FixedSingle时自定义边框样式才有效 
                        if (this.BorderStyle == BorderStyle.FixedSingle)
                        {
                            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                            using (Pen pen = new Pen(Color.FromArgb(200, this._BorderColor)))
                            {
                                if (IsFocused || f_Calendar.Visible)
                                {
                                    pen.Color = this._HotColor;
                                }
                                //绘制边框 
                                g.DrawRectangle(pen, 0, 0, this.Width - 1, this.Height - 1);


                                背景色
                               // msk_input.BackColor = _TxtBackColor;
                                //p_dropdown.BackColor = this.BackColor;

                                绘制右键按钮边框
                                //var lo_lbtn_rect = new Rectangle(p_dropdown.Left, 0, this.Width - p_dropdown.Left - 1, this.Height - 1);
                                //if (_IsRBtnMouseHover && !_IsMouseHoverDrawed)
                                //{
                                //    pen.Color = this._HotColor;
                                //}
                                //else {
                                //    pen.Color = Color.Transparent;
                                //}
                                //g.DrawRectangle(pen, lo_lbtn_rect);
                                //g.FillRectangle(new SolidBrush(_HotColor), lo_lbtn_rect);
                            }
                        }


                    }

                    //返回结果 
                    m.Result = IntPtr.Zero;
                    //释放 
                    CommonHelper.ReleaseDC(m.HWnd, hDC);

                    break;
                case CommonHelper.WM_KILLFOCUS: //失去焦点
                    if (m.HWnd != this.Handle) { return; }
                    
                    if (msk_input.Focused || p_dropdown.Focused || f_Calendar.Focused || this.Focused) { return; }
                    f_Calendar.Hide();
                    _isDropDown = false;
                    break;
                case CommonHelper.WM_LBUTTONDOWN:
                    break;
                default:
                    break;

            }

           
          
        }
        protected void drawUI() {
            if (this.Width < 21) { this.Width = 60; }
            if (this.Height < 21) { this.Height = 21; }
            p_dropdown.Height = this.Height;

            msk_input.Left =0;
            msk_input.Top = this.Height  - msk_input.Height - 2;
            msk_input.Width = this.Width - p_dropdown.Width;
            msk_input.Height = this.Height;

            p_dropdown.Left = msk_input.Left + msk_input.Width;
            p_dropdown.Top = this.Height / 2 - p_dropdown.Height / 2;
            Invalidate();

        }

        private void DateBox_SizeChanged(object sender, EventArgs e)
        {
            drawUI();
        }

        public void ReDraw() {
            Invalidate();
        }
        private void p_dropdown_Click(object sender, EventArgs e)
        {
            showCalendar();

        }
        //2、手写像事件方法一样的方法
        private void GetFocusEvent(object sender, EventArgs e)
        {
            
            msk_input.Focus();
            this.Invalidate();
        }

        private void LostFocusEvent(object sender, EventArgs e)
        {
            //失去焦点要执行的代码
            if (!p_dropdown.Focused && !f_Calendar.IsFocused && !this.Focused && !msk_input.Focused) {
                f_Calendar.Hide();
                _isDropDown = false;
                Invalidate();
            }
        }

        protected void showCalendar()
        {
            var lo_rect = RectangleToScreen(this.ClientRectangle);
            f_Calendar.ShowPosition = new Point(lo_rect.X, lo_rect.Y + this.Height);
            f_Calendar.refreshValue(_value);
         
            if (!_isDropDown)
            {
                if (f_Calendar.IsValid)
                {
                    f_Calendar.showForm(this.FindForm());
                }
                else
                {
                    f_Calendar.Show(this.FindForm());
                }
                _isDropDown = true;
                f_Calendar.Focus();
            }
            else
            {
                f_Calendar.Hide();
                _isDropDown = false;
            }
            Invalidate();
        }

        
  
        protected override void DestroyHandle()
        {
            f_Calendar.Dispose();
            base.DestroyHandle();
        }

        private void DateBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter) {
                showCalendar();

            }
        }

        private void msk_input_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                showCalendar();
                this.Focus();
                Invalidate();
            }
        }

        private void DateBox_MouseDown(object sender, MouseEventArgs e)
        {

            MouseDownOut(sender, e);
        }

        protected void MouseDownOut(object sender, MouseEventArgs e) {
            var lo_rect = RectangleToScreen(this.ClientRectangle);
            if (_isMouseLeave && !lo_rect.Contains(PointToScreen(e.Location)) && !f_Calendar.Focused)
            {
                f_Calendar.Hide();
                _isDropDown = false;
                this.Capture = false;
                Invalidate();
            }
            _isMouseLeave = false;
        }

        private void DateBox_MouseMove(object sender, MouseEventArgs e)
        {
            //鼠标状态 
           // this._IsMouseOver = true;
            //如果启用HotTrack,则开始重绘 
            //如果不加判断这里不加判断,则当不启用HotTrack, 
            //鼠标在控件上移动时,控件边框会不断重绘, 
            //导致控件边框闪烁。下同 
            //谁有更好的办法?Please tell me , Thanks。 
            //if (this._HotTrack)
           // {
                //重绘 
                //this.Invalidate();
           // }
            //base.OnMouseMove(e);
        }

        private void DateBox_MouseLeave(object sender, EventArgs e)
        {
            //_isMouseLeave = true;
            //if (!this.Capture) { this.Capture = true; }
        }

        private void msk_input_MouseLeave(object sender, EventArgs e)
        {
            //_isMouseLeave = true;
            //if (!this.Capture) { this.Capture = true; }
        }

        private void p_dropdown_MouseLeave(object sender, EventArgs e)
        {
            p_dropdown.BackColor = Color.FromArgb(0, Color.Transparent);
        }

        private void msk_input_MouseDown(object sender, MouseEventArgs e)
        {
            MouseDownOut(sender, e);
        }

        private void DateBox_MouseEnter(object sender, EventArgs e)
        {
            //this.Capture = false;
            //_isMouseLeave = false;
        }

        private void msk_input_MouseEnter(object sender, EventArgs e)
        {
            //this.Capture = false;
            //_isMouseLeave = false;
        }

        private void p_dropdown_MouseEnter(object sender, EventArgs e)
        {
            //this.Capture = false;
            //_isMouseLeave = false;
        }

        private void DateBox_Resize(object sender, EventArgs e)
        {
            if (!f_Calendar.isEmpty()) {
                if (f_Calendar.IsHandleCreated)
                    f_Calendar.Hide();
                _isDropDown = false;
            }
        
        }

        private void DateBox_LocationChanged(object sender, EventArgs e)
        {
            if (!f_Calendar.isEmpty())
            {
                if (f_Calendar.IsHandleCreated)
                    f_Calendar.Hide();
                _isDropDown = false;
            }
        }

        private void DateBox_ParentChanged(object sender, EventArgs e)
        {
            if (!f_Calendar.isEmpty())
            {
                if (f_Calendar.IsHandleCreated)
                    f_Calendar.Hide();
                _isDropDown = false;
            }
        }

        private void p_dropdown_MouseHover(object sender, EventArgs e)
        {
            p_dropdown.BackColor = Color.FromArgb(60, _HotColor);
        }

        private void p_dropdown_MouseUp(object sender, MouseEventArgs e)
        {
            p_dropdown.BackColor = Color.FromArgb(60, _HotColor);
        }

        private void p_dropdown_MouseDown(object sender, MouseEventArgs e)
        {
            p_dropdown.BackColor = Color.FromArgb(120, _HotColor);
        }

        private void msk_input_TextChanged(object sender, EventArgs e)
        {
            validateInput();
        }

        private void validateInput() {
            if (_validateTimer.isEmpty()) {
                _validateTimer = new Timer();
                _validateTimer.Tick += new EventHandler(validateInputTick);
                _validateTimer.Interval = 200;
                _validateTimer.Start();
            }
        }

        private string formatToMatchPattern(string format) {
            string ls_return = "";


            return ls_return;
        }
        private void validateInputTick(object sender, EventArgs e) {
            var ls_inputChk = msk_input.Text.isEmpty() ? "" : msk_input.Text.Replace("-", "").Replace("/", "").Trim();
            if (ls_inputChk.isEmpty()) {
                tip_error.Active = false;
                msk_input.BackColor = _TxtBackColor;
                this.BackColor = _TxtBackColor;
                _validateTimer.Stop();
                _validateTimer.Dispose();
                _validateTimer = null;
                _value = null;
                return;
            }
            var ls_paterrn = _matchPattern.ToUpper().Replace("Y", "[\\d]").Replace("M", "[\\d]").Replace("D", "[\\d]");
            var lo_match = Regex.Matches(msk_input.Text, ls_paterrn, RegexOptions.IgnoreCase);
            if (lo_match.isEmpty())
            {
                tip_error.Active = true;
                this.BackColor = Color.LightSalmon;
                msk_input.BackColor = Color.LightSalmon;
                _value = null;
            }
            else {
              

                //验证是否是有效的日期
                DateTime lo_convert;
                var ls_format = _format.Replace("/", "-").Trim();
                var ls_inputText = msk_input.Text.Replace("/", "-").Trim();
                if (ls_format.equalIngnoreCase("YYYY-MM")) { ls_inputText = ls_inputText + "-01"; }
                if (ls_format.equalIngnoreCase("MM-DD")) { ls_inputText = "1991-" + ls_inputText; }
                var lb_valid= DateTime.TryParse(ls_inputText, out lo_convert);
                if (!lb_valid)
                {
                    tip_error.Active = true;
                    this.BackColor = Color.LightSalmon;
                    msk_input.BackColor = Color.LightSalmon;
                    _value = null;
                }
                else {
                    tip_error.Active = false;
                    msk_input.BackColor = _TxtBackColor;
                    this.BackColor = _TxtBackColor;
                    _validateTimer.Stop();
                    _validateTimer.Dispose();
                    _validateTimer = null;
                    _value = lo_convert;
                    //msk_input.Text = Convert.ToDateTime(_value).ToString(_format.Replace("Y", "y").Replace("D", "d"));
                    f_Calendar.refreshValue(_value);
                }
            }

        }

        protected override void OnHandleDestroyed(EventArgs e)
        {
            if (!_validateTimer.isEmpty()) { _validateTimer.Dispose(); _validateTimer = null; }
            base.OnHandleDestroyed(e);
        }


    }
}

演示效果:

C#Winform自定义日期控件(可清空)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LoveLearnling

您的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值