Files
KJD/TEMUKJD/UserComponent/UcNumericRange.cs
T
2026-11-05 18:50:13 +08:00

160 lines
6.1 KiB
C#

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace TemuKJD.UserComponent
{
[DefaultEvent(nameof(ValueChanged))]
public class UcNumericRange : UserControl
{
private const decimal DefaultMaximum = 1000000m;
private const int NormalWidth = 220;
private const int CompactWidth = 112;
private readonly Label separator = new Label { Text = "至", AutoSize = true, Anchor = AnchorStyles.None };
private readonly NumericUpDown lower = CreateEditor("起始值");
private readonly NumericUpDown upper = CreateEditor("结束值");
private bool updating;
public UcNumericRange()
{
upper.Value = DefaultMaximum;
var layout = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 3, RowCount = 1 };
layout.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50));
layout.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50));
layout.Controls.Add(CreateCell(lower), 0, 0);
layout.Controls.Add(separator, 1, 0);
layout.Controls.Add(CreateCell(upper), 2, 0);
Controls.Add(layout);
Size = new Size(NormalWidth, lower.PreferredHeight + 2);
lower.ValueChanged += (s, e) => UpdateRange(lower, upper);
upper.ValueChanged += (s, e) => UpdateRange(upper, lower);
}
[Description("两个数值之间显示的文字。"), DefaultValue("至")]
public string SeparatorText
{
get => separator.Text;
set => separator.Text = value;
}
[Description("使用窄布局并隐藏微调按钮,仍可用方向键调整数值。"), DefaultValue(false)]
public bool Compact
{
get => separator.Margin == Padding.Empty;
set
{
if (value == Compact) return;
separator.Margin = value ? Padding.Empty : new Padding(3, 0, 3, 0);
SetCompactBorder(lower, value);
SetCompactBorder(upper, value);
if (Dock == DockStyle.None && Width == (value ? NormalWidth : CompactWidth))
Width = value ? CompactWidth : NormalWidth;
}
}
private static void SetCompactBorder(NumericUpDown editor, bool compact)
{
var cell = (Panel)editor.Parent;
editor.BorderStyle = compact ? BorderStyle.None : BorderStyle.Fixed3D;
cell.BorderStyle = compact ? BorderStyle.FixedSingle : BorderStyle.None;
cell.BackColor = compact ? editor.BackColor : Color.Empty;
cell.PerformLayout();
}
private Panel CreateCell(NumericUpDown editor)
{
var cell = new Panel { Dock = DockStyle.Fill, Margin = Padding.Empty };
cell.Controls.Add(editor);
var buttons = editor.Controls[0] is TextBox ? editor.Controls[1] : editor.Controls[0];
// 紧凑模式将右侧按钮移出可见区域,保留原生数字输入和键盘操作。
cell.Layout += (s, e) =>
{
int clippedWidth = Compact ? editor.Width - buttons.Left : 0;
editor.Width = cell.ClientSize.Width + clippedWidth;
editor.Top = Compact ? Math.Max(0, (cell.ClientSize.Height - editor.Height) / 2) : 0;
};
return cell;
}
private static NumericUpDown CreateEditor(string name)
{
return new NumericUpDown { AccessibleName = name, Margin = Padding.Empty, Maximum = DefaultMaximum };
}
[Description("允许输入的最小值。"), DefaultValue(typeof(decimal), "0")]
public decimal Minimum
{
get => lower.Minimum;
set => SetLimits(value, Math.Max(value, Maximum));
}
[Description("允许输入的最大值。"), DefaultValue(typeof(decimal), "1000000")]
public decimal Maximum
{
get => lower.Maximum;
set => SetLimits(Math.Min(value, Minimum), value);
}
[Description("选中范围的起始值。"), DefaultValue(typeof(decimal), "0")]
public decimal LowerValue
{
get => lower.Value;
set => lower.Value = value;
}
[Description("选中范围的结束值。"), DefaultValue(typeof(decimal), "1000000")]
public decimal UpperValue
{
get => upper.Value;
set => upper.Value = value;
}
[Description("数字输入框显示的小数位数。"), DefaultValue(0)]
public int DecimalPlaces
{
get => lower.DecimalPlaces;
set
{
if (value < 0 || value > 28) throw new ArgumentOutOfRangeException(nameof(value));
lower.DecimalPlaces = upper.DecimalPlaces = value;
decimal increment = 1m;
for (int i = 0; i < value; i++)
increment /= 10m;
lower.Increment = upper.Increment = increment;
}
}
public event EventHandler ValueChanged;
private void SetLimits(decimal minimum, decimal maximum)
{
decimal oldLower = LowerValue, oldUpper = UpperValue;
updating = true;
try
{
lower.Minimum = upper.Minimum = minimum;
lower.Maximum = upper.Maximum = maximum;
}
finally { updating = false; }
if (LowerValue != oldLower || UpperValue != oldUpper)
ValueChanged?.Invoke(this, EventArgs.Empty);
}
private void UpdateRange(NumericUpDown edited, NumericUpDown other)
{
if (updating) return;
updating = true;
try
{
if (LowerValue > UpperValue)
other.Value = edited.Value;
}
finally { updating = false; }
ValueChanged?.Invoke(this, EventArgs.Empty);
}
}
}