/*******************************************************************************
* Copyright(c) 2014 DongkeSoft All rights reserved. / Confidential
* 类的信息:
* 1.程序名称:C_TXT_Digital.cs
* 2.功能描述:数字文本框
* 编辑履历:
* 作者 日期 版本 修改内容
* 陈晓野 2014/09/29 1.00 新建
*******************************************************************************/
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Drawing;
using System.Globalization;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Windows.Forms;
namespace Dongke.IBOSS.PRD.Basics.BaseControls
{
///
/// 数字文本框
///
public partial class C_TXT_Digital : DKTextBoxBase
{
#region 常量
///
/// [^{0}{1}0-9]
///
private const string REJECT_CHARS = "[^{0}{1}0-9]";
///
/// [-]
///
private const string NEGATIVE = "-";
///
/// [.]
///
private const string D_POINT = ".";
///
/// [,]
///
private const string D_COMMA = ",";
#endregion
#region 成员变量
private Point _number = Point.Empty;
private decimal _maxValue = decimal.MaxValue;
private decimal _minValue = decimal.MinValue;
private decimal _maxNumberValue = decimal.MaxValue;
private decimal _minNumberValue = decimal.MinValue;
private decimal? _dataValue = null;
private bool _allowNegative = true;
private bool _allowZero = true;
private bool _hasComma = true;
private bool _showDecimal = true;
private bool _isEnter = false;
#endregion
#region 构造函数
///
/// 构造函数
///
public C_TXT_Digital()
{
InitializeComponent();
this.ContinueWndProc = false;
// 屏蔽输入法
this.ImeMode = System.Windows.Forms.ImeMode.Off;
//this.TextAlign = HorizontalAlignment.Right;
// 设正则
this.SetRejectCharsPattern();
}
public void InitControl()
{
this._number = Point.Empty;
this._maxValue = decimal.MaxValue;
this._minValue = decimal.MinValue;
this._maxNumberValue = decimal.MaxValue;
this._minNumberValue = decimal.MinValue;
this.SetRejectCharsPattern();
}
#endregion
#region 属性
///
/// 获取或设置数字的精度,Number.X为整数位的有效位数,Number.Y为小数位的有效位数
///
[Description("获取或设置数字的精度,Number.X为整数位的有效位数,Number.Y为小数位的有效位数"), Category("CustomerEx")]
[DefaultValue(typeof(Point), "0, 0")]
public Point Number
{
get
{
return this._number;
}
set
{
// Reject改变text
if (this._number != value)
{
this._number = value;
bool needReset = this.SetNumberValue();
needReset = !this.SetRejectCharsPattern() && needReset;
//if (needReset)
//{
// this.SetText(this.Text);
//}
this.SetText(this.Text);
}
}
}
///
/// 获取或设置允许输入的数字最大值
///
[Description("获取或设置允许输入的数字最大值"), Category("CustomerEx")]
[DefaultValue(typeof(decimal), "MaxValue")]
public decimal MaxValue
{
get
{
return this._maxValue;
}
set
{
// Reject改变text
//value = Math.Min(value, this._maxNumberValue);
if (value < this._minValue)
{
return;
}
if (this._maxValue != value)
{
this._maxValue = value;
bool needReset = this.SetNumberValue();
if (needReset)
{
this.SetText(this.Text);
}
}
}
}
///
/// 获取或设置允许输入的数字最小值
///
[Description("获取或设置允许输入的数字最小值"), Category("CustomerEx")]
[DefaultValue(typeof(decimal), "MinValue")]
public decimal MinValue
{
get
{
return this._minValue;
}
set
{
// Reject改变text
//value = Math.Max(value, this._minNumberValue);
if (value > this._maxValue)
{
return;
}
if (this._minValue != value)
{
this._minValue = value;
bool needReset = this.SetNumberValue();
if (needReset)
{
this.SetText(this.Text);
}
}
}
}
///
/// 获取或设置文本框的数字
///
[Description("获取或设置文本框的数字"), Category("CustomerEx")]
[DefaultValue(null)]
public decimal? DataValue
{
get
{
return this._dataValue;
}
set
{
// 改变text
if (this._dataValue != value)
{
this._dataValue = value;
this.SetText((this._dataValue == null) ? "" : this._dataValue.ToString());
}
}
}
///
/// 获取或设置是否允许输入负数
///
[Description("获取或设置是否允许输入负数"), Category("CustomerEx")]
[DefaultValue(true)]
public bool AllowNegative
{
get
{
return this._allowNegative;
}
set
{
// 改变text
if (this._allowNegative != value)
{
this._allowNegative = value;
bool needReset = this.SetNumberValue();
needReset = !this.SetRejectCharsPattern() && needReset;
if (needReset)
{
this.SetText(this.Text);
}
}
}
}
///
/// 获取或设置是否允许零值
///
[Description("获取或设置是否允许零值"), Category("CustomerEx")]
[DefaultValue(true)]
public bool AllowZero
{
get
{
return this._allowZero;
}
set
{
if (this._allowZero != value)
{
this._allowZero = value;
this.SetText(this.Text);
}
}
}
///
/// 获取或设置是否有千分符(逗号)
///
[Description("获取或设置是否有千分符(逗号)"), Category("CustomerEx")]
[DefaultValue(true)]
public bool HasComma
{
get
{
return this._hasComma;
}
set
{
if (this._hasComma != value)
{
this._hasComma = value;
this.SetText(this.Text);
}
}
}
///
/// 获取或设置是否显示补齐小数有效位数
///
[Description("获取或设置是否显示补齐小数有效位数"), Category("CustomerEx")]
[DefaultValue(true)]
public bool ShowDecimal
{
get
{
return this._showDecimal;
}
set
{
if (this._showDecimal != value)
{
this._showDecimal = value;
this.SetText(this.Text);
}
}
}
#endregion
#region 事件处理
#endregion
#region 公有方法/函数
#endregion
#region 受保护方法/函数
///
/// 限制输入文本
///
///
///
protected virtual string RejectDigitalChars(string text)
{
if (string.IsNullOrEmpty(text))
{
this._dataValue = null;
return text;
}
// 过滤非数字
string reject = "[^-.0-9]";
text = Regex.Replace(text, reject, string.Empty);
// 负号处理
if (this._allowNegative && text.StartsWith(NEGATIVE))
{
text = NEGATIVE + text.Replace(NEGATIVE, string.Empty);
}
else
{
text = text.Replace(NEGATIVE, string.Empty);
}
// 小数点处理
//if (this._number.Y > 0 && text.Contains(D_POINT))
//{
// int pointIndex = text.IndexOf(D_POINT);
// text = text.Replace(D_POINT, string.Empty);
// text = text.Insert(pointIndex, D_POINT);
//}
//else
//{
// text = text.Replace(D_POINT, string.Empty);
//}
if (text.Contains(D_POINT))
{
int pointIndex = text.IndexOf(D_POINT);
text = text.Replace(D_POINT, string.Empty);
text = text.Insert(pointIndex, D_POINT);
}
if (!this._isEnter)
{
// 数值限制
decimal value;
if (decimal.TryParse(text, out value))
{
if (value > this._maxNumberValue)
{
value = this._maxNumberValue;
}
if (value < this._minNumberValue)
{
value = this._minNumberValue;
}
string dec = null;
//if (this._number.Y > 0 && this._showDecimal)
//{
// if (this._showDecimal)
// {
// dec = D_POINT.PadRight(this._number.Y + 1, '0');
// }
// else
// {
// dec = ".#";
// }
//}
if (this._number.Y > 0)
{
if (this._showDecimal)
{
dec = D_POINT.PadRight(this._number.Y + 1, '0');
}
else
{
dec = D_POINT.PadRight(this._number.Y + 1, '#');
}
}
string comma = null;
if (this._hasComma)
{
comma = "#,##0";
text = value.ToString(comma + dec);
}
else
{
comma = "####0";
text = value.ToString(comma + dec);
}
//this._dataValue = value;
this._dataValue = Convert.ToDecimal(text);
}
else
{
this._dataValue = null;
}
}
return text;
}
///
/// 校验输入文本
///
///
///
protected override bool CheckText(string text)
{
bool check = base.CheckText(text);
if (!check)
{
return false;
}
if (!this._allowZero && this._dataValue.HasValue && this._dataValue == 0)
{
this.HasError = true;
this.ErrorMessage = "不能输入零";
return false;
}
this.HasError = false;
this.ErrorMessage = string.Empty;
return true;
}
///
/// 设置文本
///
///
protected override bool SetText(string text)
{
this.TextValue = null;
text = this.RejectDigitalChars(text);
if (this.CheckText(text))
{
this.TextValue = text;
}
if (base.Text != text)
{
this.IsSetText = true;
int selectionStart = this.SelectionStart;
int length = 0;
if (this._isEnter)
{
length = base.Text.Length - text.Length;
}
base.Text = text;
if (this._isEnter)
{
int start = selectionStart - length;
this.SelectionStart = (start > 0 ? start : 0);
}
return true;
}
return true;
}
protected override void OnEnter(EventArgs e)
{
this._isEnter = true;
this.SetText(this.Text);
base.OnEnter(e);
}
protected override void OnLeave(EventArgs e)
{
this._isEnter = false;
this.SetText(this.Text);
base.OnLeave(e);
}
#endregion
#region 私有方法/函数
private bool SetNumberValue()
{
if (this._number.IsEmpty)
{
this._maxNumberValue = decimal.MaxValue;
this._minNumberValue = (this._allowNegative ? decimal.MinValue : 0);
}
else
{
this._maxNumberValue = DNumber.ToDecimal(this._number);//this._number.ToDecimal() ;
this._minNumberValue = (this._allowNegative ? 0 - this._maxNumberValue : 0);
}
bool isChanged = false;
if (this._maxValue < this._maxNumberValue)
{
isChanged = true;
this._maxNumberValue = this._maxValue;
}
if (this._minValue > this._minNumberValue)
{
isChanged = true;
this._minNumberValue = this._minValue;
}
return isChanged;
}
///
/// 设置输入现状
///
private bool SetRejectCharsPattern()
{
string rejectChars = string.Format(REJECT_CHARS,
this._allowNegative ? NEGATIVE : string.Empty,
this._number.Y > 0 ? "." : string.Empty);
if (base.RejectChars != rejectChars)
{
base.RejectChars = rejectChars;
return true;
}
else
{
return false;
}
}
#endregion
}
///
/// 小数精度
///
[TypeConverter(typeof(DNumberConverter))]
[Serializable]
public struct DNumber
{
private int _x;
private int _y;
///
/// 整数位精度
///
public int X
{
get
{
return this._x;
}
set
{
if (value > 0)
{
this._x = value;
}
else
{
this._x = 0;
}
}
}
///
/// 小数位精度
///
public int Y
{
get
{
return this._y;
}
set
{
if (value > 0)
{
this._y = value;
}
else
{
this._y = 0;
}
}
}
///
/// 是否为空值
///
[Browsable(false)]
public bool IsEmpty
{
get
{
return this._x == 0 && this._y == 0;
}
}
public static readonly DNumber Empty = new DNumber();
///
/// 是否相等
///
///
///
public override bool Equals(object obj)
{
if ((obj is DNumber))
{
DNumber n = (DNumber)obj;
return n.X == this._x && n.Y == this._y;
}
return false;
}
public override int GetHashCode()
{
return this._x ^ this._y;
}
///
/// 比较两个 Number 对象。此结果指定两个 Number 对象的 Number.X 或 Number.Y 属性的值是否不等。
///
/// 要比较的 Number
/// 要比较的 Number
/// 如果 left 和 right 的 Number.X 属性值或 Number.Y 属性值不等,则为true;否则为 false
public static bool operator !=(DNumber left, DNumber right)
{
return (left.X != right.X || left.Y != right.Y);
}
///
/// 比较两个 Number 对象。此结果指定两个 Number 对象的 Number.X 或 Number.Y 属性的值是否相等。
///
/// 要比较的 Number
/// 要比较的 Number
/// 如果 left 和 right 的 Number.X 属性值或 Number.Y 属性值不等,则为true;否则为 false
public static bool operator ==(DNumber left, DNumber right)
{
return (left.X == right.X && left.Y == right.Y);
}
///
/// 构造
///
///
///
public DNumber(int x, int y)
{
if (x > 0)
{
this._x = x;
}
else
{
this._x = 0;
}
if (y > 0)
{
this._y = y;
}
else
{
this._y = 0;
}
}
///
/// 最大数值
///
///
public decimal ToDecimal()
{
if (!this.IsEmpty)
{
string dec = null;
if (this._x > 0)
{
dec = string.Empty.PadLeft(this._x, '9');
}
else
{
dec = "0";
}
if (this._y > 0)
{
dec += ".".PadRight(this._y + 1, '9');
}
return Convert.ToDecimal(dec);
}
return 0;
}
public static decimal ToDecimal(Point p)
{
if (!p.IsEmpty)
{
string dec = null;
if (p.X > 0)
{
dec = string.Empty.PadLeft(p.X, '9');
}
else
{
dec = "0";
}
if (p.Y > 0)
{
dec += ".".PadRight(p.Y + 1, '9');
}
return Convert.ToDecimal(dec);
}
return 0;
}
public override string ToString()
{
return string.Concat(new string[]
{
"{X=",
this.X.ToString(CultureInfo.CurrentCulture),
",Y=",
this.Y.ToString(CultureInfo.CurrentCulture),
"}"
});
}
}
///
/// Converts a object from one data type to another. Access this class through the object.
///
/// 1
public class DNumberConverter : TypeConverter
{
///
/// Determines if this converter can convert an object in the given source type to the native type of the converter.
///
/// true if this object can perform the conversion; otherwise, false.
///
///
/// A formatter context. This object can be used to get additional information about the environment this converter is being called from. This may be null, so you should always check. Also, properties on the context object may also return null.
///
///
/// The type you want to convert from.
///
/// 1
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
///
/// Gets a value indicating whether this converter can convert an object to the given destination type using the context.
///
/// true if this converter can perform the conversion; otherwise, false.
///
///
/// An object that provides a format context.
///
///
/// A object that represents the type you want to convert to.
///
/// 1
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType);
}
///
/// Converts the specified object to a object.
///
///
/// The converted object.
///
///
/// A formatter context. This object can be used to get additional information about the environment this converter is being called from. This may be null, so you should always check. Also, properties on the context object may also return null.
///
///
/// An object that contains culture specific information, such as the language, calendar, and cultural conventions associated with a specific culture. It is based on the RFC 1766 standard.
///
///
/// The object to convert.
///
///
/// The conversion cannot be completed.
///
/// 1
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
string text = value as string;
if (text == null)
{
return base.ConvertFrom(context, culture, value);
}
string text2 = text.Trim();
if (text2.Length == 0)
{
return null;
}
if (culture == null)
{
culture = CultureInfo.CurrentCulture;
}
char c = culture.TextInfo.ListSeparator[0];
string[] array = text2.Split(new char[]
{
c
});
int[] array2 = new int[array.Length];
TypeConverter converter = TypeDescriptor.GetConverter(typeof(int));
for (int i = 0; i < array2.Length; i++)
{
array2[i] = (int)converter.ConvertFromString(context, culture, array[i]);
}
if (array2.Length == 2)
{
return new DNumber(array2[0], array2[1]);
}
//throw new ArgumentException(SR.GetString("TextParseFailedFormat", new object[]
//{
// text2,
// "x, y"
//}));
throw new ArgumentException("TextParseFailedFormat");
}
///
/// Converts the specified object to the specified type.
///
///
/// The converted object.
///
///
/// A formatter context. This object can be used to get additional information about the environment this converter is being called from. This may be null, so you should always check. Also, properties on the context object may also return null.
///
///
/// An object that contains culture specific information, such as the language, calendar, and cultural conventions associated with a specific culture. It is based on the RFC 1766 standard.
///
///
/// The object to convert.
///
///
/// The type to convert the object to.
///
///
/// The conversion cannot be completed.
///
/// 1
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == null)
{
throw new ArgumentNullException("destinationType");
}
if (value is DNumber)
{
if (destinationType == typeof(string))
{
DNumber dnumber = (DNumber)value;
if (culture == null)
{
culture = CultureInfo.CurrentCulture;
}
string separator = culture.TextInfo.ListSeparator + " ";
TypeConverter converter = TypeDescriptor.GetConverter(typeof(int));
string[] array = new string[2];
int num = 0;
array[num++] = converter.ConvertToString(context, culture, dnumber.X);
array[num++] = converter.ConvertToString(context, culture, dnumber.Y);
return string.Join(separator, array);
}
if (destinationType == typeof(InstanceDescriptor))
{
DNumber dnumber2 = (DNumber)value;
ConstructorInfo constructor = typeof(DNumber).GetConstructor(new Type[]
{
typeof(int),
typeof(int)
});
if (constructor != null)
{
return new InstanceDescriptor(constructor, new object[]
{
dnumber2.X,
dnumber2.Y
});
}
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
///
/// Creates an instance of this type given a set of property values for the object.
///
///
/// The newly created object, or null if the object could not be created. The default implementation returns null.
///
///
/// A type descriptor through which additional context can be provided.
///
///
/// A dictionary of new property values. The dictionary contains a series of name-value pairs, one for each property returned from .
///
/// 1
public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
{
if (propertyValues == null)
{
throw new ArgumentNullException("propertyValues");
}
object obj = propertyValues["X"];
object obj2 = propertyValues["Y"];
if (obj == null || obj2 == null || !(obj is int) || !(obj2 is int))
{
throw new ArgumentException("PropertyValueInvalidEntry");
}
return new DNumber((int)obj, (int)obj2);
}
///
/// Determines if changing a value on this object should require a call to to create a new value.
///
/// true if the method should be called when a change is made to one or more properties of this object; otherwise, false.
///
///
/// A through which additional context can be provided.
///
/// 1
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
{
return true;
}
///
/// Retrieves the set of properties for this type. By default, a type does not return any properties.
///
///
/// The set of properties that are exposed for this data type. If no properties are exposed, this method might return null. The default implementation always returns null.
///
///
/// A type descriptor through which additional context can be provided.
///
///
/// The value of the object to get the properties for.
///
///
/// An array of objects that describe the properties.
///
/// 1
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(DNumber), attributes);
return properties.Sort(new string[]
{
"X",
"Y"
});
}
///
/// Determines if this object supports properties. By default, this is false.
///
/// true if should be called to find the properties of this object; otherwise, false.
///
///
/// A through which additional context can be provided.
///
/// 1
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
}
}