C#Winform 可清空自定义日期控件
程序员文章站
2022-06-07 13:36:45
...
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自定义日期控件(可清空)
下一篇: WPF 如何在代码中使用自定义的鼠标资源