自定义控件TextBox之DecimalBox

自定义控件TextBox之DecimalBox
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( thisnew BeyondLimitEventArgs(tmp, DecimalBoxBeyondKind.BeyondMinValue));
             else  if (limit == 1)
                OnBeyondLimit( thisnew 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,
    }
}


你可能感兴趣的:(自定义控件TextBox之DecimalBox)