本來想用MaskTextBox,但發現障礙太多,還是用TextBox好作
2010/9/29 考量處理效能和數值範圍,修正使用型別由decimal改為double
2010/9/29 考量處理效能和數值範圍,修正使用型別由decimal改為double
繼承.Net內建TextBox再拿來繼承,增加以下:
key-in或貼上同時排除不合格文字或數字
一些不適合數字的屬性和Text改唯讀,
增加屬性: 最大,
最小值,
預設值,
整數位數上限,
小數位數上限,
Value
Format(點進NumBox時顯示原數字,離開後顯示格式化數字),
允許空值,
錯誤嗶聲,
輸入值型別{所有數值型別}台灣是主權獨立的國家
增加事件:OnValueChanged
public class SanNumBox : 繼承TextBox
{
public SanNumBox()
{//建構子直接對變數存取就好了~
textType = typeof(Int32);
min = Convert.ToDouble(Int32.MinValue);//最小
max = Convert.ToDouble(Int32.MaxValue);//最大
def = 0;//預設
DecLen = 2;//小數幾位數
int1 = 10;//整數幾位數;至少1位
AllowNull = true;
HasComma = false;
this.TextAlign = HorizontalAlignment.Right;
}
#region Properties
private const char Hyphen = '-';
private const char Dot = '.';
private const string CommaFormat = "#,##0.######";
private const string O = "0";
#region Hidden base不可修改
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[ReadOnly(true)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Multiline
{ get { return base.Multiline; } }
//繼承TextBox時建立的
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[ReadOnly(true)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool MaxLenByByte
{ get { return base.MaxLenByByte; } }
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new bool WordWrap
{ get { return base.WordWrap; } }
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never)]
[ReadOnly(true)]
public override int MaxLength
{ get { return base.MaxLength; } }
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never)]
[ReadOnly(true)]
public new string[] Lines
{ get { return base.Lines; } }
#endregion
#region overried 可改
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[ReadOnly(true)]
public override string Text
{
get { return base.Text; }
set { this.Value = value; }
}
[DefaultValue(HorizontalAlignment.Right)]
public new HorizontalAlignment TextAlign
{
set { base.TextAlign = value; }
get { return base.TextAlign; }
}
#endregion
#region 自訂Field 可修改
#region Max
private Double max;
/// <summary>
/// 最大值(上限值)
/// </summary>
[Description(@"最大值(上限值)")]
[Category("Customize")]
[EditorBrowsable(EditorBrowsableState.Always)]
public Double MaxValue
{
set
{
OverflowException exc = this.SetMax(value);
OverflowException exc2 = this.SetDefault(this.DefaultValue);
this.Value = this.Value;//有例外直接丟
if (exc != null) { throw exc; }
if (exc2 != null) { throw exc2; }
}
get { return max; }
}
/// <summary>
/// 設最大值
/// </summary>
/// <param name="value">設定值</param>
/// <param name="throwExc">有例外是否放在return值</param>
/// <returns></returns>
private OverflowException SetMax(Double value)
{
max = Math.Min(this.GetMax(), value);
if (max < min) { return new OverflowException("MaxValue小於MinValue"); ; }
if (max == value) { return null; }
return new OverflowException("MaxValue超出該設定型態之最大值");
}
#endregion
#region Min
private Double min;
/// <summary>
/// 最小值(下限值)
/// </summary>
[Description(@"最小值(下限值)")]
[Category("Customize")]
[EditorBrowsable(EditorBrowsableState.Always)]
public Double MinValue
{
set
{
OverflowException exc = this.SetMin(value);
OverflowException exc2 = this.SetDefault(this.DefaultValue);
this.Value = this.Value;//有例外直接丟
if (exc != null) { throw exc; }
if (exc2 != null) { throw exc2; }
}
get
{ return min; }
}
/// <summary>
/// 設最小值
/// </summary>
/// <param name="value">設定值</param>
/// <param name="throwExc">有例外是否放在return值</param>
/// <returns></returns>
private OverflowException SetMin(Double value)
{
min = Math.Max(this.GetMin(), value);
if (max < min) { return new OverflowException("MinValue大於MaxValue"); ; }
if (min == value) { return null; }
return new OverflowException("MinValue超出該設定型態之最小值");
}
#endregion
#region default
private Double def;
/// <summary>
/// 預設值
/// </summary>
[Description(@"預設值")]
[Category("Customize")]
[EditorBrowsable(EditorBrowsableState.Always)]
public Double DefaultValue
{
set
{//預設值不超過上下限
OverflowException exc = this.SetDefault(value);
if (exc != null) { throw exc; }
}
get
{ return def; }
}
/// <summary>
/// 設default值
/// </summary>
/// <param name="value">設定值</param>
/// <param name="throwExc">有例外是否放在return值</param>
/// <returns></returns>
private OverflowException SetDefault(Double value)
{
def = Math.Min(Math.Max(value, this.MinValue), this.MaxValue);
if (def == value) { return null; }
return new OverflowException("DefaultValue超出所設定之最大或最小值");
}
#endregion
#region 值
private object m_value;//值
/// <summary>
/// 值
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public object Value
{
set
{
Exception exc = this.SetValue(value);
if (exc != null) { throw exc; }
}
get
{ return m_value; }
}
/// <summary>
/// 設value值
/// </summary>
/// <param name="value">設定值</param>
/// <returns></returns>
private Exception SetValue(object value)
{
Exception exc = null;
object odValue = this.m_value;
//空值或是只輸入"-"
if (!Information.IsNumeric(value))
{
if (this.AllowNull) { this.m_value = null; }
else { this.m_value = Convert.ChangeType(this.DefaultValue, TextType); }
}
else
{
Double dec = Convert.ToDouble(value);
if (dec < this.MinValue || dec > this.MaxValue)
#region 超過上下限
{
//依設定處理
switch (this.OverFlowToDo)
{
case OverFlow.returnDefault:
m_value = Convert.ChangeType(this.DefaultValue, TextType);
break;
case OverFlow.returnLimit:
m_value = Convert.ChangeType(Math.Min(Math.Max(dec, this.MinValue), this.MaxValue), TextType);
break;
case OverFlow.returnNull:
m_value = null;
break;
case OverFlow.throwException:
exc = new OverflowException("Value超過設定之最大值或最小值");
break;
}
}
#endregion
else
{ m_value = Convert.ChangeType(Convert.ToDouble(value), TextType); }
}
ShowText(IsFocused);
//觸發ValueChanged事件
if (this.m_value != odValue)
{ this.OnValueChanged(new EventArgs()); }
return exc;
}
#endregion
#region Mask
/// <summary>
/// 小數幾位數
/// </summary>
[Description(@"小數幾位數")]
[Category("Mask")]
[EditorBrowsable(EditorBrowsableState.Always)]
public byte DecLen
{ set; get; }
private byte int1;
/// <summary>
/// 整數幾位數
/// </summary>
[Description(@"整數幾位數")]
[Category("Mask")]
[EditorBrowsable(EditorBrowsableState.Always)]
public byte IntLen
{
set
{ int1 = Math.Min(Byte.MaxValue, value); }
get
{ return int1; }
}
/// <summary>
/// 輸入時是否要show千分位
/// </summary>
[Description(@"輸入時是否要show千分位")]
[Category("Mask")]
[DefaultValue(false)]
[EditorBrowsable(EditorBrowsableState.Always)]
public bool HasComma
{ set; get; }
#endregion
/// <summary>
/// 超出上下限時
/// </summary>
[Description(@"超出上下限時")]
[Category("Customize")]
[Bindable(true)]
[Browsable(true)]
[DefaultValue(OverFlow.throwException)]
[EditorBrowsable(EditorBrowsableState.Always)]
public OverFlow OverFlowToDo//OverFlow是Enum,寫在code最下方
{ set; get; }
/// <summary>
/// FormatString
/// </summary>
[Description(@"FormatString")]
[Category("Customize")]
[Bindable(true)]
[Browsable(true)]
[DefaultValue("")]
[EditorBrowsable(EditorBrowsableState.Always)]
public string Format
{ set; get; }
/// <summary>
/// 允許key空值
/// </summary>
[Description(@"允許key空值")]
[Category("Customize")]
[Bindable(true)]
[Browsable(true)]
[DefaultValue(true)]
[EditorBrowsable(EditorBrowsableState.Always)]
public bool AllowNull
{ set; get; }
private Type textType;
/// <summary>
/// 輸入值型別
/// </summary>
[Description(@"輸入值型別")]
[Category("Customize")]
[Bindable(true)]
[Browsable(true)]
[DefaultValue(typeof(int))]
[EditorBrowsable(EditorBrowsableState.Always)]
public Type TextType
{
set
{
//public static bool IsNum(Type ty)
//{
//if (ty == typeof(DateTime)) { return false; }
//if (ty.GetField("MinValue") == null || ty.GetField("MaxValue") == null) { return false; }
//return ty.IsValueType;
//} 另外有提出來寫
if (IsNum(value))
{
this.textType = value;//改型別之後,要接著改最大最小和預設值
OverflowException exc = this.SetMax(MaxValue);
OverflowException exc1 = this.SetMin(MinValue);
OverflowException exc2 = this.SetDefault(this.DefaultValue);
this.Value = this.Value;//有例外直接丟
if (exc != null) { throw exc; }
if (exc1 != null) { throw exc1; }
if (exc2 != null) { throw exc2; }
}
}
get { return this.textType; }
}
#endregion
#endregion
#region Events
#region ValueChanged Events
/// <summary>
/// ValueChange事件
/// </summary>
[Description(@"值變更時")]
[Category("Focus")]
[EditorBrowsable(EditorBrowsableState.Advanced)]
public event EventHandler ValueChanged;
protected virtual void OnValueChanged(EventArgs e)
{ if (this.ValueChanged != null) { ValueChanged(this, e); } }
#endregion
/// <summary>
/// 是否被focus.
/// enter:true, leave:false, validating取消: true, validated判斷為true時,格式化Text,
/// enter判斷為false時,格式化Text
/// </summary>
private bool IsFocused = false;
protected override void OnEnter(EventArgs e)
{
if (!IsFocused)
{
IsFocused = true;
ShowText(true);
}
base.OnEnter(e);
}
#region 編輯
/// <summary>
/// 不要作動作
/// </summary>
private bool dontSet = false;
/// <summary>
/// 只是個負號
/// </summary>
/// <returns></returns>
private bool JustNSign(string str)
{ return (str.TrimStart(Hyphen) == string.Empty); }
protected override void OnTextChanged(EventArgs e)
{
if (dontSet || JustNSign(base.Text) || base.Text.EndsWith(Dot.ToString()) ||
(base.Text == string.Empty && this.Focused)) { return; }
int idx = this.SelectionStart;//原游標停留位置
idx = base.Text.Length - idx;//倒過來數數
string str = base.Text;//原值
Exception ex = SetValue(str);
if (ex != null) { this.beep(); }
//在繼承TextBox已有寫
///// <summary>
///// 嗶一聲
///// </summary>
//protected virtual void beep()
//{ if (this.BeepOnError) { Interaction.Beep(); } }
if (base.Text != str)
{ this.SelectionStart = Math.Max(0, this.Text.Length - idx); }
}
#region keyboad事件
/// <summary>
/// 表示按著不放
/// </summary>
public bool LongClick
{ set; get; }
/// <summary>
/// 檢查每個key-in
/// </summary>
/// <param name="e"></param>
protected override void OnKeyPress(KeyPressEventArgs e)
{
if (!onpaste && LongClick)
{//因code觸發,不處理
e.Handled = true;
return;
}
try
{
if (ReadOnly) return; //唯讀不處理
if (char.IsControl(e.KeyChar))
{//倒退鍵可以一直按著
if (e.KeyChar != ValueUtil.back) { LongClick = true; }
return;
} //Backspace, Enter...等控制鍵不處理
if (e.KeyChar <= '9' && e.KeyChar >= '0')
#region 判斷整數和小數有沒有打超過
{
int idx = base.Text.IndexOf(Dot);//小數點位置
if (idx < 0)//沒打小數
{
if (base.Text.Trim(Hyphen).Length
- this.SelectedText.Trim(Hyphen).Length < IntLen) { return; }
}
else//有打小數的話
{
if (this.SelectionStart <= idx)//打在整數位置
{
if (base.Text.StartsWith(Hyphen.ToString())) { idx--; }//負號不算入整數長度
if (idx < IntLen) { return; }//已輸入整數比設的整數長度少:可再輸入
}
//且已輸入小數長度base.Text.Length-1-idx比設的小數位少:可再輸入
else if (base.Text.Length - 1 - idx < DecLen) { return; }//打在小數位置
}
}
#endregion
//若能key負號,負號可以key在開頭
else if (this.MinValue < 0 && this.SelectionStart == 0 && e.KeyChar == Hyphen) { return; }
else if (this.DecLen > 0 && e.KeyChar == Dot)
#region 若能key小數,只能打一個小數點
{
int idx = base.Text.IndexOf(Dot);//小數點位置
if (idx < 0) { return; }//未輸入
else
{
if (this.SelectionStart == idx)
{//若輸入的位置後就有小數點, 往後移一位
this.SelectionStart = idx + 1;
e.Handled = true;
return;
}
//else不能有第2個小數點
}
}
#endregion
#region 其它都不可key
if (!onpaste) { LongClick = true; }
e.Handled = true;
beep();
#endregion
}
finally
{ if (!e.Handled) { base.OnKeyPress(e); } }
}
protected override void OnKeyUp(KeyEventArgs e)
{
LongClick = false;//放開按鍵
base.OnKeyUp(e);
}
#endregion
/// <summary>
/// 不觸發keypass,此方法在繼承TextBox時新建的
/// </summary>
private bool onpaste = false;
protected override void OnPasteText(ClipboardEventArgs e)
{
onpaste = true;
e.Cancel = true;
base.SendCharFromClipboard();
onpaste = false;
}
#region select All
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (!e.Handled && e.Control && e.KeyCode == Keys.A)//ctl+a 全選
{ this.SelectAll(); }
}
#endregion
#endregion
#region 離開
protected override void OnLeave(EventArgs e)
{
IsFocused = false;
base.OnLeave(e);
}
/// <summary>
/// 若不允許null不可離開
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected override void OnValidating(CancelEventArgs e)
{
if (base.Text == string.Empty)
{
if (AllowNull)
{
Exception exc = this.SetValue(null);
if (exc != null) { beCancel(e); }
}
else
{ beCancel(e); }
}
if (!e.Cancel) { base.OnValidating(e); }
}
private void beCancel(CancelEventArgs e)
{
beep();
e.Cancel = true;
IsFocused = true;
}
protected override void OnValidated(EventArgs e)
{
base.OnValidated(e);
if (!IsFocused) { ShowText(false); }
}
#endregion
#endregion
#region Private Methods
#region 顯示文字
/// <summary>
/// 顯示文字格式化
/// </summary>
/// <param name="focus"></param>
private void ShowText(bool focus)
{
dontSet = true;
if (this.Value == null)
{ base.Text = string.Empty; }
else if (focus)
{ base.Text = Convert.ToDouble(this.Value).ToString(formatNum()); }
else
{ base.Text = Convert.ToDouble(this.Value).ToString(this.Format); }
dontSet = false;
}
/// <summary>
/// 依設定能輸入的整數和小數與是否要千分位來格式化數字
/// </summary>
/// <returns></returns>
private string formatNum()
{
return (this.HasComma ? CommaFormat : O)
+ (this.DecLen > 0 ? Dot.ToString().PadRight(this.DecLen + 1, '#') : string.Empty);
}
#endregion
#region 取其type內建值
/// <summary>
/// 取得最大值
/// </summary>
/// <returns></returns>
private Double GetMax()
{ try { return GetFieldValue("MaxValue"); } catch { return Double.MaxValue; } }
/// <summary>
/// 取得最小值
/// </summary>
/// <returns></returns>
private Double GetMin()
{ try { return GetFieldValue("MinValue"); } catch { return Double.MinValue; } }
/// <summary>
/// 取得變數值
/// </summary>
/// <param name="FieldName"></param>
/// <returns></returns>
private Double GetFieldValue(string FieldName)
{ return Convert.ToDouble(TextType.GetField(FieldName).GetValue(null)); }
#endregion
#endregion
}
/// <summary>
/// 超出上下限時所作的動作
/// </summary>
public enum OverFlow
{
/// <summary>
/// 丟出例外
/// </summary>
throwException,
/// <summary>
/// 回傳null值
/// </summary>
returnNull,
/// <summary>
/// 回傳預設值
/// </summary>
returnDefault,
/// <summary>
/// 回傳上下限值
/// </summary>
returnLimit
}
Taiwan is a country. 臺灣是我的國家