// COPYRIGHT (C) Tom. ALL RIGHTS RESERVED. // THE AntdUI PROJECT IS AN WINFORM LIBRARY LICENSED UNDER THE Apache-2.0 License. // LICENSED UNDER THE Apache License, VERSION 2.0 (THE "License") // YOU MAY NOT USE THIS FILE EXCEPT IN COMPLIANCE WITH THE License. // YOU MAY OBTAIN A COPY OF THE LICENSE AT // // http://www.apache.org/licenses/LICENSE-2.0 // // UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, SOFTWARE // DISTRIBUTED UNDER THE LICENSE IS DISTRIBUTED ON AN "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. // SEE THE LICENSE FOR THE SPECIFIC LANGUAGE GOVERNING PERMISSIONS AND // LIMITATIONS UNDER THE License. // GITEE: https://gitee.com/antdui/AntdUI // GITHUB: https://github.com/AntdUI/AntdUI // CSDN: https://blog.csdn.net/v_132 // QQ: 17379620 using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Design; using System.Windows.Forms; namespace AntdUI { /// /// DatePickerRange 日期范围选择框 /// /// 输入或选择日期范围的控件。 [Description("DatePickerRange 日期范围选择框")] [ToolboxItem(true)] [DefaultProperty("Value")] [DefaultEvent("ValueChanged")] public class DatePickerRange : Input, SubLayeredForm { #region 属性 bool showS = true, showE = true; string? placeholderS = null, placeholderE = null; /// /// 显示的水印文本 /// [Description("显示的水印文本S"), Category("行为"), DefaultValue(null)] public string? PlaceholderStart { get => placeholderS; set { if (placeholderS == value) return; placeholderS = value; Invalidate(); } } /// /// 显示的水印文本 /// [Description("显示的水印文本E"), Category("行为"), DefaultValue(null)] public string? PlaceholderEnd { get => placeholderE; set { if (placeholderE == value) return; placeholderE = value; Invalidate(); } } /// /// 水印文本 /// [Browsable(false), Description("水印文本"), Category("行为"), DefaultValue(null)] public override string? PlaceholderText => null; string dateFormat = "yyyy-MM-dd"; bool ShowTime = false; /// /// 格式化 /// [Description("格式化"), Category("行为"), DefaultValue("yyyy-MM-dd")] public string Format { get => dateFormat; set { if (dateFormat == value) return; dateFormat = value; ShowTime = dateFormat.Contains("H"); } } DateTime[]? _value = null; [Description("控件当前日期"), Category("数据"), DefaultValue(null)] public DateTime[]? Value { get => _value; set { _value = value; ValueChanged?.Invoke(this, new DateTimesEventArgs(value)); if (value == null) Text = ""; else Text = value[0].ToString(Format) + "\t" + value[1].ToString(Format); } } /// /// 最小日期 /// [Description("最小日期"), Category("数据"), DefaultValue(null)] public DateTime? MinDate { get; set; } /// /// 最大日期 /// [Description("最大日期"), Category("数据"), DefaultValue(null)] public DateTime? MaxDate { get; set; } protected override void OnTextChanged(EventArgs e) { if (isempty) showS = showE = true; else { string text = Text; int index = text.IndexOf("\t"); if (index > -1) { showS = index == 0; showE = string.IsNullOrEmpty(text.Substring(index + 1)); } else showS = showE = false; } base.OnTextChanged(e); } protected override bool Verify(char key, out string? change) { if (StartFocused) { int index = Text.IndexOf("\t"); if (index != -1) { if (SelectionStart > index) SelectionStart = index; } } else if (EndFocused) { int index = Text.IndexOf("\t"); if (index == -1) { change = "\t" + key; return true; } } return base.Verify(key, out change); } string? swapSvg = null; /// /// 交换图标SVG /// [Description("交换图标SVG"), Category("外观"), DefaultValue(null)] public string? SwapSvg { get => swapSvg; set { if (swapSvg == value) return; swapSvg = value; Invalidate(); } } /// /// 日期徽标回调 /// public Func?>? BadgeAction = null; BaseCollection? items; /// /// 预置 /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] [Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor", typeof(UITypeEditor))] [Description("预置"), Category("数据"), DefaultValue(null)] public BaseCollection Presets { get { items ??= new BaseCollection(); return items; } set => items = value; } /// /// 菜单弹出位置 /// [Description("菜单弹出位置"), Category("行为"), DefaultValue(TAlignFrom.BL)] public TAlignFrom Placement { get; set; } = TAlignFrom.BL; /// /// 下拉箭头是否显示 /// [Description("下拉箭头是否显示"), Category("外观"), DefaultValue(true)] public bool DropDownArrow { get; set; } = true; /// /// 焦点时展开下拉 /// [Description("焦点时展开下拉"), Category("行为"), DefaultValue(true)] public bool FocusExpandDropdown { get; set; } = true; protected override void OnHandleCreated(EventArgs e) { if (_value != null) Text = _value[0].ToString(Format) + "\t" + _value[1].ToString(Format); base.OnHandleCreated(e); } #region 自带图标 bool showicon = true; /// /// 是否显示图标 /// [Description("是否显示图标"), Category("外观"), DefaultValue(true)] public bool ShowIcon { get => showicon; set { if (showicon == value) return; showicon = value; CalculateRect(); Invalidate(); } } public override bool HasSuffix { get => showicon; } protected override void PaintRIcon(Graphics g, Rectangle rect_r) { if (showicon) { using (var bmp = SvgDb.IcoDate.SvgToBmp(rect_r.Width, rect_r.Height, Style.Db.TextQuaternary)) { if (bmp == null) return; g.DrawImage(bmp, rect_r); } } } #endregion #endregion #region 事件 public event DateTimesEventHandler? ValueChanged; /// /// 预置点击时发生 /// [Description("预置点击时发生"), Category("行为")] public event ObjectNEventHandler? PresetsClickChanged = null; #endregion #region 焦点 bool textFocus = false; bool TextFocus { get => textFocus; set { if (textFocus == value) return; textFocus = value; if (!ReadOnly && value) { if (subForm == null) { if (ShowTime) { subForm = new LayeredFormCalendarTimeRange(this, ReadRectangle, _value, date => { Value = date; }, btn => { PresetsClickChanged?.Invoke(this, new ObjectNEventArgs(btn)); }, BadgeAction); subForm.Disposed += (a, b) => { subForm = null; TextFocus = false; }; subForm.Show(this); } else { subForm = new LayeredFormCalendarRange(this, ReadRectangle, _value, date => { Value = date; }, btn => { PresetsClickChanged?.Invoke(this, new ObjectNEventArgs(btn)); }, BadgeAction); subForm.Disposed += (a, b) => { subForm = null; TextFocus = false; }; subForm.Show(this); } } } else subForm?.IClose(); } } protected override void OnGotFocus(EventArgs e) { base.OnGotFocus(e); if (!StartFocused && !EndFocused) StartFocused = true; StartEndFocused(); if (FocusExpandDropdown) TextFocus = true; } protected override void OnLostFocus(EventArgs e) { base.OnLostFocus(e); TextFocus = StartFocused = EndFocused = false; StartEndFocused(); AnimationBarValue = RectangleF.Empty; if (IsHandleCreated) { string text = Text; int index = text.IndexOf("\t"); if (index > 0) { string stext = text.Substring(0, index), etext = text.Substring(index + 1); if (DateTime.TryParse(stext, out var date_s) && DateTime.TryParse(etext, out var date_e)) Value = new DateTime[] { date_s, date_e }; else { if (_value == null) Text = ""; else Text = _value[0].ToString(Format) + "\t" + _value[1].ToString(Format); } } } } #region 动画 ILayeredForm? subForm = null; public ILayeredForm? SubForm() => subForm; #endregion #endregion #region 鼠标 protected override void OnClearValue() => Value = null; protected override void OnFocusClick(bool SetFocus) { if (HasFocus) { if (textFocus) return; TextFocus = !textFocus; } else Focus(); } protected override bool ProcessCmdKey(ref System.Windows.Forms.Message msg, Keys keyData) { if (keyData == Keys.Escape && subForm != null) { subForm.IClose(); return true; } else if (keyData == Keys.Enter) { if (StartFocused || EndFocused) { string text = Text; int index = text.IndexOf("\t"); if (StartFocused) { string stext; if (index == -1) stext = text; else stext = text.Substring(0, index); if (DateTime.TryParse(stext, out var date_s)) { if (index == -1) { Text = date_s.ToString(Format) + '\t'; SelectionStart = Text.Length; } else { var etext = text.Substring(index + 1); if (DateTime.TryParse(etext, out var date_e)) Text = date_s.ToString(Format) + '\t' + date_e.ToString(Format); else Text = date_s.ToString(Format) + '\t' + etext; } if (subForm is LayeredFormCalendarRange layered_range) { layered_range.Date = date_s; layered_range.SetDateS(date_s); layered_range.Print(); } else if (subForm is LayeredFormCalendarTimeRange layered_time) layered_time.IClose(); StartFocused = false; EndFocused = true; StartEndFocused(); SetCaretPostion(); } } else { string etext; if (index == -1) etext = text; else etext = text.Substring(index + 1); if (DateTime.TryParse(etext, out var date_e)) { if (index == -1) Text = '\t' + date_e.ToString(Format); else { string stext = text.Substring(0, index); if (DateTime.TryParse(stext, out var date_s)) { Text = date_s.ToString(Format) + '\t' + date_e.ToString(Format); if (subForm is LayeredFormCalendarRange layered_range) { layered_range.Date = date_e; layered_range.SetDateE(date_s, date_e); layered_range.Print(); } else if (subForm is LayeredFormCalendarTimeRange layered_time) layered_time.IClose(); } else Text = text.Substring(0, index) + '\t' + date_e.ToString(Format); } } } return true; } } return base.ProcessCmdKey(ref msg, keyData); } protected override bool IMouseDown(Point e) { if (rect_d_l.Contains(e) || rect_d_ico.Contains(e)) { EndFocused = false; StartFocused = true; StartEndFocused(); } else if (rect_d_r.Contains(e)) { StartFocused = false; EndFocused = true; StartEndFocused(); } return false; } #endregion #region 渲染 protected override bool ModeRange => true; protected override void ModeRangeCaretPostion(bool Null) { if (Null) { if (TextAlign == HorizontalAlignment.Center) { if (StartFocused) SetCaretX(rect_d_l.X + rect_d_l.Width / 2); else if (EndFocused) SetCaretX(rect_d_r.X + rect_d_r.Width / 2); } else if (TextAlign == HorizontalAlignment.Right) { if (StartFocused) SetCaretX(rect_d_l.Right); else if (EndFocused) SetCaretX(rect_d_r.Right); } else { if (StartFocused) SetCaretX(rect_d_l.X); else if (EndFocused) SetCaretX(rect_d_r.X); } } else { if (StartFocused) { if (!rect_d_l.Contains(CurrentCaret)) ModeRangeCaretPostion(true); } else if (EndFocused) { if (!rect_d_r.Contains(CurrentCaret)) ModeRangeCaretPostion(true); } } } protected override void PaintOtherBor(Graphics g, RectangleF rect_read, float radius, Color back, Color borderColor, Color borderActive) { if ((showS && placeholderS != null) || (showE && placeholderE != null)) { using (var fore = new SolidBrush(Style.Db.TextQuaternary)) { if (showS && placeholderS != null) g.DrawStr(placeholderS, Font, fore, rect_d_l, sf_placeholder); if (showE && placeholderE != null) g.DrawStr(placeholderE, Font, fore, rect_d_r, sf_placeholder); } } if (AnimationBar) { float h = rect_text.Height * 0.14F; var BarColor = BorderActive ?? Style.Db.Primary; using (var brush = new SolidBrush(BarColor)) { g.FillRectangle(brush, new RectangleF(AnimationBarValue.X, rect_read.Bottom - h, AnimationBarValue.Width, h)); } } else if (StartFocused || EndFocused) { float h = rect_text.Height * 0.14F; var BarColor = BorderActive ?? Style.Db.Primary; using (var brush = new SolidBrush(BarColor)) { if (StartFocused) g.FillRectangle(brush, new RectangleF(rect_d_l.X, rect_read.Bottom - h, rect_d_l.Width, h)); else g.FillRectangle(brush, new RectangleF(rect_d_r.X, rect_read.Bottom - h, rect_d_r.Width, h)); } } g.GetImgExtend(swapSvg ?? SvgDb.IcoSwap, rect_d_ico, Style.Db.TextQuaternary); } #endregion #region 指示条 bool AnimationBar = false; RectangleF AnimationBarValue = RectangleF.Empty; ITask? ThreadBar = null; string StartEndFocusedTmp = "00"; void StartEndFocused() { bool s = StartFocused, e = EndFocused; string temp = (s ? 1 : 0).ToString() + (e ? 1 : 0).ToString(); if (StartEndFocusedTmp == temp) return; StartEndFocusedTmp = temp; if (Config.Animation && (s || e)) { RectangleF NewValue; if (s) NewValue = rect_d_l; else NewValue = rect_d_r; if (AnimationBarValue == RectangleF.Empty) AnimationBarValue = new RectangleF(NewValue.X - 10, NewValue.Y, 0, NewValue.Height); float p_val = Math.Abs(NewValue.X - AnimationBarValue.X) * 0.09F; if (p_val > 0) { float p_val2 = (NewValue.X - AnimationBarValue.X) * 0.5F, p_w_val = Math.Abs(NewValue.Width - AnimationBarValue.Width) * 0.1F; AnimationBar = true; bool left = NewValue.X > AnimationBarValue.X; ThreadBar?.Dispose(); ThreadBar = new ITask(this, () => { if (AnimationBarValue.Width != NewValue.Width) { AnimationBarValue.Width += p_w_val; if (AnimationBarValue.Width > NewValue.Width) AnimationBarValue.Width = NewValue.Width; } if (left) { if (AnimationBarValue.X > p_val2) AnimationBarValue.X += p_val / 2F; else AnimationBarValue.X += p_val; if (AnimationBarValue.X > NewValue.X) { AnimationBarValue.X = NewValue.X; Invalidate(); return false; } } else { AnimationBarValue.X -= p_val; if (AnimationBarValue.X < NewValue.X) { AnimationBarValue.X = NewValue.X; Invalidate(); return false; } } if (subForm is LayeredFormCalendarRange layered) { if (Placement == TAlignFrom.TR || Placement == TAlignFrom.BR) layered.SetArrow(AnimationBarValue.X - rect_d_r.X); else layered.SetArrow(AnimationBarValue.X); } Invalidate(); return true; }, 10, () => { if (subForm is LayeredFormCalendarRange layered) { if (Placement == TAlignFrom.TR || Placement == TAlignFrom.BR) layered.SetArrow(NewValue.X - rect_d_r.X); else layered.SetArrow(NewValue.X); } AnimationBarValue = NewValue; AnimationBar = false; Invalidate(); }); return; } } if (s) AnimationBarValue = rect_d_l; else AnimationBarValue = rect_d_r; } bool StartFocused = false, EndFocused = false; protected override void Dispose(bool disposing) { ThreadBar?.Dispose(); base.Dispose(disposing); } #endregion } }