using System.Drawing; using System.Globalization; using System.Text.Json.Nodes; using Sim.Host; namespace System.Windows.Forms; public enum DateTimePickerFormat { Long = 1, Short = 2, Time = 4, Custom = 8 } /// 日期时间选择器(教材项目 6 任务 3 选查询的起止日期)。Text 按 Format 显示,和中文 Windows 一致(Long:2024年1月16日) public class DateTimePicker : Control { public static readonly DateTime MinimumDateTime = new(1753, 1, 1); public static readonly DateTime MaximumDateTime = new(9998, 12, 31); private DateTime _value = SimClock.Now; private DateTimePickerFormat _format = DateTimePickerFormat.Long; private string? _customFormat; private bool _showUpDown; private DateTime _minDate = MinimumDateTime; private DateTime _maxDate = MaximumDateTime; protected override Size DefaultSize => new(200, 21); public DateTime Value { get => _value; set { if (value < _minDate || value > _maxDate) throw new ArgumentOutOfRangeException(nameof(value), $"“{value}”的值对于“Value”无效。“Value”应介于“MinDate”和“MaxDate”之间。"); if (value == _value) return; LegacyThreads.CheckPoint(); _value = value; NotifyValue(); OnValueChanged(EventArgs.Empty); } } public DateTimePickerFormat Format { get => _format; set { _format = value; NotifyValue(); NotifyChanged(nameof(Format), value); } } public string? CustomFormat { get => _customFormat; set { _customFormat = value; NotifyValue(); } } public bool ShowUpDown { get => _showUpDown; set => SetProp(ref _showUpDown, value, nameof(ShowUpDown)); } public DateTime MinDate { get => _minDate; set => _minDate = value; } public DateTime MaxDate { get => _maxDate; set => _maxDate = value; } public bool ShowCheckBox { get; set; } public bool Checked { get; set; } = true; public LeftRightAlignment DropDownAlign { get; set; } /// 显示的文字;赋值时按日期解析 public override string Text { get => Display; set { if (string.IsNullOrEmpty(value)) return; Value = DateTime.Parse(value, CultureInfo.CurrentCulture); } } private string Display => _format switch { DateTimePickerFormat.Short => _value.ToString("d", CultureInfo.CurrentCulture), DateTimePickerFormat.Time => _value.ToString("T", CultureInfo.CurrentCulture), DateTimePickerFormat.Custom when !string.IsNullOrEmpty(_customFormat) => _value.ToString(_customFormat, CultureInfo.CurrentCulture), _ => _value.ToString("D", CultureInfo.CurrentCulture), }; public event EventHandler? ValueChanged; public event EventHandler? CloseUp; public event EventHandler? DropDown; protected virtual void OnValueChanged(EventArgs e) => ValueChanged?.Invoke(this, e); /// 浏览器里选了新的日期或时间(ISO 格式,本地时间):和 WinForms 一样先 ValueChanged,再 CloseUp protected override void OnUserAction(UserAction action) { switch (action.Action) { case "dropDown": DropDown?.Invoke(this, EventArgs.Empty); break; case "pick" when DateTime.TryParse(action.Value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var picked): var clamped = picked < _minDate ? _minDate : picked > _maxDate ? _maxDate : picked; if (clamped != _value) Value = clamped; else NotifyValue(); CloseUp?.Invoke(this, EventArgs.Empty); break; default: base.OnUserAction(action); break; } } private void NotifyValue() { NotifyChanged("Text", Display); NotifyChanged("Value", _value.ToString("yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture)); } protected override void WriteProps(JsonObject props) { base.WriteProps(props); props["Value"] = _value.ToString("yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture); props["Format"] = _format.ToString(); props["ShowUpDown"] = _showUpDown; props["HasTime"] = _format == DateTimePickerFormat.Time || (_format == DateTimePickerFormat.Custom && (_customFormat ?? "").IndexOfAny(['H', 'h', 'm', 's']) >= 0); props["HasDate"] = _format != DateTimePickerFormat.Time && !(_format == DateTimePickerFormat.Custom && (_customFormat ?? "").IndexOfAny(['y', 'M', 'd']) < 0); } }