欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-06-07 13:36:45
...

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

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

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

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

//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自定义日期控件(可清空)