using System;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
namespace RCRS.WinLibrary.Presentation.UI
{
//--------------------------------------------------------------------------
/// <summary>
/// 数値テキストボックス
/// </summary>
//--------------------------------------------------------------------------
[DefaultBindingProperty("Value")]
public partial class CtrlDecimalBox : TextBox
{
private decimal? maxValue = 99999.99M;
private decimal? minValue = 0.0M;
private decimal? value = null;
private int? decimalDigits = null;
private char decimalSeparator = '.';
private char groupSeparator = ',';
private char negativeSign = '-';
private bool useGroup = true;
private bool useLimit = false;
//--------------------------------------------------------------
/// <summary>
/// バリューが限界を超えるデリゲートです。
/// </summary>
//--------------------------------------------------------------
public delegate void DecimalBoxBeyondLimitHandler(object sender, BeyondLimitEventArgs bevent);
//--------------------------------------------------------------
/// <summary>
/// バリューが限界を超えたら、発生します。
/// </summary>
//--------------------------------------------------------------
public event DecimalBoxBeyondLimitHandler BeyondLimit;
//--------------------------------------------------------------
/// <summary>
/// Valueがチェンジしたら、発生します。
/// </summary>
//--------------------------------------------------------------
public event EventHandler ValueChanged;
#region 属性設定
//--------------------------------------------------------------
/// <summary>
/// 整数部分をグループ区切る記号で分けるかのプロパティ
/// </summary>
//--------------------------------------------------------------
[DefaultValue(true)]
[Description("整数部分をグループ区切る記号で分けるかどうか")]
[Category("DecimalSetting")]
public bool UseGroup
{
get { return useGroup; }
set { useGroup = value; }
}
//--------------------------------------------------------------
/// <summary>
/// 小数点以下桁数を設定します。
/// Nullにしたら桁数の制限がなくなる
/// </summary>
//--------------------------------------------------------------
[DefaultValue(null)]
[Description("小数点以下桁数を設定します。Nullにしたら桁数の制限がなくなる")]
[Category("DecimalSetting")]
public int? DecimalDigits
{
get { return decimalDigits; }
set
{
if (value == null)
decimalDigits = value;
else if (value < 0)
decimalDigits = null;
else
decimalDigits = value;
}
}
//--------------------------------------------------------------
/// <summary>
/// 限界値を使用するかのプロパティ
/// </summary>
//--------------------------------------------------------------
[DefaultValue(false)]
[Description("限界値を使用するかどうか")]
[Category("DecimalSetting")]
public bool UseLimit
{
get { return useLimit; }
set { useLimit = value; }
}
//--------------------------------------------------------------
/// <summary>
/// 最大値のプロパティ
/// Nullにしたら最大値を判断しません
/// </summary>
//--------------------------------------------------------------
[DefaultValue(typeof(decimal), "99999.99")]
[Description("最大値のプロパティ,Nullにしたら最大値を判断しません")]
[Category("DecimalSetting")]
public decimal? MaxValue
{
get { return maxValue; }
set
{
if (value == null)
maxValue = value;
else if (value <= minValue)
maxValue = minValue + 1;
else
maxValue = value;
}
}
//--------------------------------------------------------------
/// <summary>
/// 最小値のプロパティ
/// Nullにしたら最小値を判断しません
/// </summary>
//--------------------------------------------------------------
[DefaultValue(typeof(decimal), "0.0")]
[Description("最小値のプロパティ,Nullにしたら最小値を判断しません")]
[Category("DecimalSetting")]
public decimal? MinValue
{
get { return minValue; }
set
{
if (value == null)
minValue = value;
else if (value >= maxValue)
minValue = maxValue - 1;
else
minValue = value;
}
}
//--------------------------------------------------------------
/// <summary>
/// 整数と小数を区切る記号(.)
/// </summary>
//--------------------------------------------------------------
[DefaultValue('.')]
[Description("整数と小数を区切る記号(.)")]
[Category("DecimalSetting")]
public char DecimalSeparator
{
get { return decimalSeparator; }
set
{
if (value != decimalSeparator &&
value != groupSeparator &&
value != negativeSign)
decimalSeparator = value;
}
}
//--------------------------------------------------------------
/// <summary>
/// 整数グループを区切る記号(,)
/// </summary>
//--------------------------------------------------------------
[DefaultValue(',')]
[Description("整数グループを区切る記号(,)")]
[Category("DecimalSetting")]
public char GroupSeparator
{
get { return groupSeparator; }
set
{
if (value != decimalSeparator &&
value != groupSeparator &&
value != negativeSign)
groupSeparator = value;
}
}
//--------------------------------------------------------------
/// <summary>
/// 数値が負であることを示す記号(-)
/// </summary>
//--------------------------------------------------------------
[DefaultValue('-')]
[Description("数値が負であることを示す記号(-)")]
[Category("DecimalSetting")]
public char NegativeSign
{
get { return negativeSign; }
set
{
if (value != decimalSeparator &&
value != groupSeparator &&
value != negativeSign)
negativeSign = value;
}
}
//--------------------------------------------------------------
/// <summary>
/// 数字値のプロパティ
/// </summary>
//--------------------------------------------------------------
[Bindable(true)]
[DefaultValue("")]
[Description("数字値のプロパティ")]
[Category("DecimalSetting")]
public string Value
{
get { return value.ToString(); }
set { setValue(value, true); }
}
//--------------------------------------------------------------
/// <summary>
/// 親クラスのプロパティです、DataBindできないように処理されました。
/// </summary>
//--------------------------------------------------------------
[Bindable(false)]
[DefaultValue("")]
public override string Text
{
get { return base.Text; }
set { base.Text = value; }
}
#endregion
//--------------------------------------------------------------
/// <summary>
/// CtrlDecimalBoxのインスタンスを作成します。
/// </summary>
//--------------------------------------------------------------
public CtrlDecimalBox()
{
InitializeComponent();
ImeMode = ImeMode.Disable;
DataBindings.CollectionChanged += new CollectionChangeEventHandler(DataBindings_CollectionChanged);
}
//--------------------------------------------------------------
/// <summary>
/// valueが変わったときの処理メソッド
/// </summary>
//--------------------------------------------------------------
protected virtual void OnValueChanged(object sender, EventArgs eventArgs)
{
if (ValueChanged != null)
ValueChanged(this, EventArgs.Empty);
}
//--------------------------------------------------------------
/// <summary>
/// valueのあたいが限界を超えたら、発生します。
/// </summary>
//--------------------------------------------------------------
protected virtual void OnBeyondLimit(object sender, BeyondLimitEventArgs bevent)
{
if (BeyondLimit != null)
BeyondLimit(this, bevent);
}
//--------------------------------------------------------------
/// <summary>
/// コントロールにエンターするとき、Control.Enterイベントを発生させて、カンマを抜きます。
/// </summary>
//--------------------------------------------------------------
protected override void OnEnter(EventArgs e)
{
base.OnEnter(e);
string tmpStr = stringtoNumStr(Text);
Text = tmpStr;
}
//--------------------------------------------------------------
/// <summary>
/// Control.KeyDownイベントを発生させます。
/// </summary>
//--------------------------------------------------------------
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
}
//--------------------------------------------------------------
/// <summary>
/// Control.KeyPressイベントを発生させて、許容されるキーだけを処理します。
/// </summary>
//--------------------------------------------------------------
protected override void OnKeyPress(KeyPressEventArgs e)
{
base.OnKeyPress(e);
if (Char.IsDigit(e.KeyChar) && //数字が入力されたとき
((Text.Contains(negativeSign) &&
SelectionStart != 0) || //「-」があり先頭以外にカーソルがあるとき
((Text.Contains(negativeSign) &&
SelectionStart == 0) &&
0 < SelectionLength) || //「-」があり先頭から文字列が選択状態のとき
(!Text.Contains(negativeSign)))) // 「-」がないとき
{
}
else if (e.KeyChar == '\b')
{
}
else if (e.KeyChar == decimalSeparator &&
!Text.Contains(decimalSeparator) &&
SelectionStart != 0 &&
decimalDigits != 0)
{
}
else if (e.KeyChar == negativeSign &&
!Text.Contains(negativeSign) &&
SelectionStart == 0)
{
}
else if (e.KeyChar == negativeSign && //全選択した状態で、「-」マイナスが入力できるように追加します。
SelectionStart == 0 &&
SelectionLength > 0)
{
}
else
{
e.Handled = true;
}
}
//--------------------------------------------------------------
/// <summary>
/// テキストを変更して、Control.KeyUpイベントを発生させます。
/// </summary>
//--------------------------------------------------------------
protected override void OnKeyUp(KeyEventArgs e)
{
if (!e.Handled)
{
string tmpStr = stringtoNumStr(Text);
if (tmpStr != "-") //1文字目「-」のとき入力許可
setValue(tmpStr, false);
}
base.OnKeyUp(e);
}
//--------------------------------------------------------------
/// <summary>
/// テキストボックスがフォーカスを失ったとき、発生します。
/// </summary>
/// <param name="e"></param>
//--------------------------------------------------------------
protected override void OnLostFocus(EventArgs e)
{
handleText();
base.OnLostFocus(e);
}
//--------------------------------------------------------------
/// <summary>
/// データバインディング
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
//--------------------------------------------------------------
private void DataBindings_CollectionChanged(object sender, CollectionChangeEventArgs e)
{
if (e.Element != null)
{
if (e.Action == CollectionChangeAction.Add)
((Binding)e.Element).Parse += new ConvertEventHandler(CtrlDecimalBox_Parse);
else if (e.Action == CollectionChangeAction.Remove)
((Binding)e.Element).Parse -= CtrlDecimalBox_Parse;
}
}
//--------------------------------------------------------------
/// <summary>
/// バインディングのParseイベントハンドラー
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
//--------------------------------------------------------------
private void CtrlDecimalBox_Parse(object sender, ConvertEventArgs e)
{
if (e.Value.ToString() == "")
e.Value = DBNull.Value;
}
//--------------------------------------------------------------
/// <summary>
/// 文字列から数字へ変換して、valueに値を付ける。
/// </summary>
/// <param name="strValue">Valueに付けたい値</param>
/// <param name="changText">テキストを更新しますか</param>
//--------------------------------------------------------------
private void setValue(string strValue, bool changText)
{
decimal? tmp = null;
bool isNullableNum = true;
//strValueをNullableDecimalに変換します。
if (strValue == "" || strValue == null)
tmp = null;
else
{
decimal tmp1;
if (pretreatment(strValue, out tmp1))
tmp = tmp1;
else
isNullableNum = false;
}
int limit = 0;
//変換成功したかどうかを判断して、処理します。
if (isNullableNum)
{
if (value != tmp)
{
value = tmp;
limit = handleBeyondLimit();
OnValueChanged(this, EventArgs.Empty);
}
if (!Focused)
Text = numbertoString(value);
}
else
{
if (changText)
Text = numbertoString(value);
else
Text = eraseComma(numbertoString(value));
}
//限界を超えたら、BeyondLimitイベントを発生させます。
if (limit == -1)
OnBeyondLimit(this, new BeyondLimitEventArgs(tmp, DecimalBoxBeyondKind.BeyondMinValue));
else if (limit == 1)
OnBeyondLimit(this, new BeyondLimitEventArgs(tmp, DecimalBoxBeyondKind.BeyondMaxValue));
}
//--------------------------------------------------------------
/// <summary>
/// 科学的表記法された数字を処理して、デシマルに変換。
/// </summary>
/// <param name="strValue">入力された数字(文字列型)</param>
/// <param name="outDec">出力される数字</param>
/// <returns>処理成功したかどうか</returns>
//--------------------------------------------------------------
private bool pretreatment(string strValue, out decimal outDec)
{
string tmpStr = strValue.ToLower();
decimal number = 0m;
decimal index = 0m;
bool result = false;
try
{
if (tmpStr.Contains("e"))
{
number = Convert.ToDecimal(tmpStr.Split('e')[0]);
index = Convert.ToDecimal(tmpStr.Split('e')[1]);
for (int i = 0; Math.Abs(i) < Math.Abs(index); )
{
if (index > 0)
{
number = number * 10m;
i++;
}
else
{
number = number / 10m;
i--;
}
}
strValue = number.ToString();
}
if (Decimal.TryParse(strValue, out number))
result = true;
else
result = false;
}
catch
{
result = false;
}
outDec = number;
return result;
}
//--------------------------------------------------------------
/// <summary>
/// 限界を越えたかどうかを判断し、バリューを処理します。
/// 超えなかったら0で、最大値より大きいなら1で、最小値より小さいなら-1です。
/// </summary>
//--------------------------------------------------------------
private int handleBeyondLimit()
{
int result = 0;
if (useLimit && value != null)
{
if (minValue != null && value < minValue)
{
value = minValue;
result = -1;
}
else if (maxValue != null && value > maxValue)
{
value = maxValue;
result = 1;
}
}
return result;
}
//--------------------------------------------------------------
/// <summary>
/// 変わったテキストを処理します。
/// </summary>
//--------------------------------------------------------------
private void handleText()
{
string tmpStr;
tmpStr = stringtoNumStr(Text);
setValue(tmpStr, true);
}
//--------------------------------------------------------------
/// <summary>
/// テキストを標準数字の文字列に変換します(カンマなし)
/// </summary>
/// <param name="str">変換したい文字列</param>
//--------------------------------------------------------------
private string stringtoNumStr(string str)
{
string decimalPart = "";
string sign = "";
if (str.Contains(negativeSign))
{
sign = "-";
str = str.Replace(negativeSign.ToString(), "");
}
str = str.Replace(groupSeparator.ToString(), "");
str = str.Replace(decimalSeparator.ToString(), ".");
if (str.Contains('.'))
{
decimalPart = str.Split('.')[1];
if (decimalPart.Length != 0)
{
if (decimalDigits != null)
{
if (decimalDigits < decimalPart.Length)
{
str = str.Remove(str.IndexOf('.') + (int)decimalDigits + 1);
}
}
}
else
str = str.Replace(".", "");
}
str = sign + str;
return str;
}
//--------------------------------------------------------------
/// <summary>
/// コントロールのプロパティによって数字を表示します。
/// </summary>
/// <param name="num">変換したい数字</param>
//--------------------------------------------------------------
private string numbertoString(decimal? num)
{
if (num != null)
{
string integerPart = "";
string decimalPart = "";
string result = "";
string point = "";
string sign = "";
//句読点を処理して、整数と小数を分けます。
result = num.ToString();
if (result.Contains('-'))
{
sign = negativeSign.ToString();
result = result.Replace("-", "");
}
if (result.Contains('.'))
{
integerPart = result.Split('.')[0];
decimalPart = result.Split('.')[1];
}
else
integerPart = result;
if ((decimalDigits == null && decimalPart != "") ||
(decimalDigits != null && decimalDigits != 0))
point = decimalSeparator.ToString();
//整数部分を処理します。
if (useGroup)
{
string tmp = "";
if (integerPart.Length % 3 != 0)
{
tmp += integerPart.Substring(0, integerPart.Length % 3);
tmp += groupSeparator.ToString();
integerPart = integerPart.Remove(0, integerPart.Length % 3);
}
for (int i = 0; i * 3 < integerPart.Length; i++)
{
tmp += integerPart.Substring(i * 3, 3);
tmp += groupSeparator.ToString();
}
tmp = tmp.Remove(tmp.Length - 1, 1);
integerPart = tmp;
}
//小数部分を処理します。
if (decimalDigits != null)
{
if (decimalPart.Length < (int)decimalDigits)
{
int tmpLength = decimalPart.Length;
for (int i = 0; i < decimalDigits - tmpLength; i++)
decimalPart += "0";
}
else
decimalPart = decimalPart.Substring(0, (int)decimalDigits);
}
//総合処理
result = sign + integerPart + point + decimalPart;
return result;
}
else
return "";
}
//--------------------------------------------------------------
/// <summary>
/// コントロールのプロパティによってフォーマットした文字列のカンマを抜きます。
/// </summary>
/// <param name="str">カンマを抜きたい文字列</param>
//--------------------------------------------------------------
private string eraseComma(string str)
{
str = str.Replace(groupSeparator.ToString(), "");
string sign = "";
if (str.Contains('-'))
{
sign = negativeSign.ToString();
str = str.Replace("-", "");
}
if (str.Contains('.'))
{
str = str.Replace('.', decimalSeparator);
}
str = sign + str;
return str;
}
}
//--------------------------------------------------------------
/// <summary>
/// BeyondLimitEventArgs、BeyondLimitイベントの引数クラスです
/// </summary>
//--------------------------------------------------------------
public class BeyondLimitEventArgs : EventArgs
{
//--------------------------------------------------------------
/// <summary>
/// BeyondLimitの種類を示す。
/// </summary>
//--------------------------------------------------------------
public DecimalBoxBeyondKind BeyondKind;
//--------------------------------------------------------------
/// <summary>
/// BeyondLimitイベントを起こすバリューです。
/// </summary>
//--------------------------------------------------------------
public decimal? Value;
//--------------------------------------------------------------
/// <summary>
/// BeyondLimitEventArgsのインスタンスを作成します。
/// </summary>
//--------------------------------------------------------------
public BeyondLimitEventArgs(decimal? value, DecimalBoxBeyondKind beyondKind)
{
Value = value;
BeyondKind = beyondKind;
}
}
//--------------------------------------------------------------
/// <summary>
/// BeyondLimitの種類を示す列挙型です。
/// </summary>
//--------------------------------------------------------------
public enum DecimalBoxBeyondKind
{
//--------------------------------------------------------------
/// <summary>
/// MaxValueを超えた。
/// </summary>
//--------------------------------------------------------------
BeyondMaxValue,
//--------------------------------------------------------------
/// <summary>
/// MinValueを超えた。
/// </summary>
//--------------------------------------------------------------
BeyondMinValue,
}
}
posted on 2016-12-23 14:20
Ying-er 阅读(171)
评论(0) 编辑 收藏 所属分类:
.Net