160 lines
6.1 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|