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,
    }
}

posted on 2016-12-23 14:20 Ying-er 阅读(171) 评论(0)  编辑  收藏 所属分类: .Net

只有注册用户登录后才能发表评论。


网站导航: