C# - 金額入力コントロール
概要
現在オフショアでやってる代物と関係のあるものだが、とりあえずサックリと金額入力コントロールを作った。
なんかこういうのとかあるけど、
http://www.c-sharpcorner.com/UploadFile/mgold/MaskedCurrencyTextBox08142005135643PM/MaskedCurrencyTextBox.aspx?ArticleID=3dc8ca4b-7166-4851-ab40-a2150c5ca787
言っちゃ悪いがしょぼくね?という事で同じくらいしょぼいのを作成中。
そのうちWitchGardenにまとめ、さらにその後Polestar行きにする予定。
その頃にはもうちょいソースコードも綺麗になっている予定。
今は要所にやる気の無さが見えるが。
ソース
using System; using System.Collections.Generic; using System.Windows.Forms; using System.ComponentModel; /// <summary> /// 金額入力欄を担当するテキストボックス /// </summary> /// <remarks> /// 現状SelectionStart、SelectionLength等の処理は未実装。ちゃんと実装すればよりマシになる。 /// 継承の設計などもちゃんとやってない(プロパティではなくメンバを見てる場所があるなど)。 /// 継承前提じゃないので、継承する場合は気合を入れてやること(個人的には継承よりソース変更を推奨)。 /// クリップボードからの貼り付けは現在かなり適当に対応中 /// </remarks> /// <logs> /// <create>2007/04/30</create> /// <lastupdate>2007/04/30</lastupdate> /// </logs> /// <author>t.tsugehara</author> public class CurrencyBox : TextBox { #region static private static Dictionary<Keys, char> KEY_VALUE = null; protected static void InitKeyValue() { KEY_VALUE = new Dictionary<Keys, char>(20); KEY_VALUE.Add(Keys.D0, '0'); KEY_VALUE.Add(Keys.D1, '1'); KEY_VALUE.Add(Keys.D2, '2'); KEY_VALUE.Add(Keys.D3, '3'); KEY_VALUE.Add(Keys.D4, '4'); KEY_VALUE.Add(Keys.D5, '5'); KEY_VALUE.Add(Keys.D6, '6'); KEY_VALUE.Add(Keys.D7, '7'); KEY_VALUE.Add(Keys.D8, '8'); KEY_VALUE.Add(Keys.D9, '9'); KEY_VALUE.Add(Keys.NumPad0, '0'); KEY_VALUE.Add(Keys.NumPad1, '1'); KEY_VALUE.Add(Keys.NumPad2, '2'); KEY_VALUE.Add(Keys.NumPad3, '3'); KEY_VALUE.Add(Keys.NumPad4, '4'); KEY_VALUE.Add(Keys.NumPad5, '5'); KEY_VALUE.Add(Keys.NumPad6, '6'); KEY_VALUE.Add(Keys.NumPad7, '7'); KEY_VALUE.Add(Keys.NumPad8, '8'); KEY_VALUE.Add(Keys.NumPad9, '9'); } #endregion /// <summary> /// Keyを処理するためのイベントハンドラ /// </summary> /// <param name="key"></param> public delegate void CurrencyKeyEventHandler(Keys key); #region private field /// <summary>Valueプロパティの実体</summary> private string m_value; /// <summary>カーソルの論理位置</summary> private int m_cursorPoint; /// <summary>Prefix</summary> private string m_prefix; /// <summary>通貨マーク</summary> private string m_currencyMark; /// <summary>セパレート挿入単位</summary> private int m_sepLength = 3; /// <summary>符号</summary> private bool m_isSign; /// <summary>BackspaceとDelete共用の削除処理</summary> CurrencyKeyEventHandler m_deleteLogicEventHandller; /// <summary>文字挿入処理</summary> CurrencyKeyEventHandler m_insertLogicEventHandller; /// <summary>カーソル移動処理</summary> CurrencyKeyEventHandler m_moveLogicEventHandller; /// <summary>符号切り替え処理</summary> CurrencyKeyEventHandler m_changeSignLogicEventHandller; /// <summary>Keyとイベントのマップ</summary> Dictionary<Keys, CurrencyKeyEventHandler> m_keyEventMap; #endregion #region Constructor public CurrencyBox() { // MEMO:微妙? if (KEY_VALUE == null) { InitKeyValue(); } // initial value this.m_value = "0"; this.m_isSign = false; this.m_keyEventMap = new Dictionary<Keys, CurrencyKeyEventHandler>(32); this.m_currencyMark = "\\"; syncPrefix(); // create event handllers this.m_deleteLogicEventHandller = new CurrencyKeyEventHandler(deleteLogic); this.m_insertLogicEventHandller = new CurrencyKeyEventHandler(insertLogic); this.m_moveLogicEventHandller = new CurrencyKeyEventHandler(moveLogic); this.m_changeSignLogicEventHandller = new CurrencyKeyEventHandler(changeSignLogic); // mapping key events this.m_keyEventMap.Add(Keys.D0, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.D1, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.D2, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.D3, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.D4, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.D5, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.D6, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.D7, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.D8, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.D9, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.NumPad0, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.NumPad1, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.NumPad2, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.NumPad3, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.NumPad4, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.NumPad5, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.NumPad6, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.NumPad7, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.NumPad8, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.NumPad9, this.m_insertLogicEventHandller); this.m_keyEventMap.Add(Keys.Back, this.m_deleteLogicEventHandller); this.m_keyEventMap.Add(Keys.Delete, this.m_deleteLogicEventHandller); this.m_keyEventMap.Add(Keys.Up, this.m_moveLogicEventHandller); this.m_keyEventMap.Add(Keys.Home, this.m_moveLogicEventHandller); this.m_keyEventMap.Add(Keys.Left, this.m_moveLogicEventHandller); this.m_keyEventMap.Add(Keys.Right, this.m_moveLogicEventHandller); this.m_keyEventMap.Add(Keys.Down, this.m_moveLogicEventHandller); this.m_keyEventMap.Add(Keys.End, this.m_moveLogicEventHandller); this.m_keyEventMap.Add(Keys.Enter, this.m_moveLogicEventHandller); this.m_keyEventMap.Add(Keys.OemMinus, this.m_changeSignLogicEventHandller); this.m_keyEventMap.Add(Keys.Subtract, this.m_changeSignLogicEventHandller); // property initial value this.TextAlign = HorizontalAlignment.Right; this.ShortcutsEnabled = false; // initial display updateText(); } #endregion #region Property [Browsable(true)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] [Description("Currency value of string")] public string Value { get { return this.m_value; } set { this.m_value = value; updateText(); } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public decimal CurrencyValue { get { decimal value = 0; try { value = decimal.Parse(this.Value); } catch (FormatException) { value = 0; } catch (OverflowException) { value = 0; } if (this.m_isSign) { value = -value; } return value; } } [Browsable(true)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] [Description("Currency mark. (ex: \\, $)")] public string CurrencyMark { get { return this.m_currencyMark; } set { this.m_currencyMark = value; syncPrefix(); } } // 全然設計してないprotected君達 protected string Prefix { get { return this.m_prefix; } } protected int SeparateLength { get { return this.m_sepLength;} set { this.m_sepLength = value; } } protected Dictionary<Keys, CurrencyKeyEventHandler> KeyEventMap { get { return this.m_keyEventMap; } } #endregion #region override raise event method // ほんとはちゃんとやりたいねぇ protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); this.SelectionStart = getSelectionStart(); this.SelectionLength = 0; } // ほんとはちゃんとやりたいねぇ protected override void OnMouseUp(MouseEventArgs mevent) { base.OnMouseUp(mevent); this.SelectionStart = getSelectionStart(); this.SelectionLength = 0; } // イベント処理とKeyPressの抹殺 protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (this.m_keyEventMap.ContainsKey(e.KeyCode)) { this.m_keyEventMap[e.KeyCode](e.KeyCode); e.SuppressKeyPress = true; } // MEMO:dust if (e.Control && (e.KeyCode == Keys.V)) { Paste(); } if (e.Shift && (e.KeyCode == Keys.Insert)) { Paste(); } } // 抹殺 protected override void OnKeyPress(KeyPressEventArgs e) { base.OnKeyPress(e); e.Handled = true; } // Focusとった時にずれるので抹殺・・。切ねぇ protected override void OnGotFocus(EventArgs e) { base.OnGotFocus(e); this.SelectionLength = 0; this.SelectionStart = getSelectionStart(); } #endregion #region virtual and non virtual protected method /// <summary>Valueの値に合わせて表示文字列を更新する</summary> protected virtual void updateText() { Char[] chars = new Char[ this.m_prefix.Length + this.Value.Length + this.Value.Length / this.m_sepLength ]; int j; for (j = 0; j < this.m_prefix.Length; j++) { chars[j] = this.m_prefix[j]; } int valueLength = this.Value.Length; for (int i = 0; i < valueLength; i++) { if ((i > 0) && ((valueLength - i) % this.m_sepLength == 0)) { chars[j++] = ','; } chars[j++] = this.Value[i]; } this.Text = new String(chars); } /// <summary> /// 論理カーソル位置から表示用カーソル位置を計算して返す /// </summary> /// <returns>SelectionStartに適用出来る値</returns> protected int getSelectionStart() { int separateCorrection; if (this.Value.Length > this.m_sepLength) { int odd = this.m_sepLength - this.Value.Length % this.m_sepLength; if (odd == this.m_sepLength) odd = 0; separateCorrection = (this.m_cursorPoint + odd) / this.m_sepLength; } else { separateCorrection = this.m_cursorPoint / this.m_sepLength; } int cursor = this.m_cursorPoint + this.m_prefix.Length + separateCorrection; return cursor; } /// <summary> /// プリフィックスの値を現在の状態に合わせて更新する /// </summary> protected void syncPrefix() { if (this.m_isSign) { this.m_prefix = this.m_currencyMark + "-"; } else { this.m_prefix = this.m_currencyMark; } } #endregion #region private event handller // 符号反転 void changeSignLogic(Keys key) { this.m_isSign = !this.m_isSign; syncPrefix(); updateText(); this.SelectionStart = getSelectionStart(); } // 文字列消去 void deleteLogic(Keys key) { if (this.Value.Length > 0) { int delP = this.m_cursorPoint; if (key == Keys.Back) { delP--; } if ((delP >= 0) && (delP < this.Value.Length)) { this.Value = this.Value.Remove( delP, 1 ); this.m_cursorPoint = delP; } } updateText(); this.SelectionStart = getSelectionStart(); } // 文字列挿入 void insertLogic(Keys key) { this.Value = this.Value.Insert( this.m_cursorPoint, KEY_VALUE[key].ToString() ); this.m_cursorPoint++; this.SelectionStart = getSelectionStart(); } // カーソル移動 void moveLogic(Keys key) { switch (key) { case Keys.Up: case Keys.Home: this.m_cursorPoint = 0; this.SelectionStart = getSelectionStart(); break; case Keys.Down: case Keys.End: this.m_cursorPoint = this.Value.Length; this.SelectionStart = getSelectionStart(); break; case Keys.Right: if (this.m_cursorPoint < this.Value.Length) { this.m_cursorPoint++; } this.SelectionStart = getSelectionStart(); break; case Keys.Left: if (this.m_cursorPoint > 0) { this.m_cursorPoint--; } this.SelectionStart = getSelectionStart(); break; case Keys.Enter: Control ctl = this.Parent.GetNextControl(this, true); while (ctl.TabStop == false) { ctl = this.Parent.GetNextControl(ctl, true); if (ctl == null) { // ?? break; } } if (ctl != null) ctl.Focus(); break; } this.SelectionLength = 0; } #endregion #region dust box // ごみ public override bool PreProcessMessage(ref Message msg) { if (msg.Msg == 0x0100) { switch ((int)msg.WParam) { case (int)Keys.Insert: // ShortcutEnabledの設定を横取り return false; case (int)Keys.V: // ShortcutEnabledの設定を横取り return false; } } return base.PreProcessMessage(ref msg); } // TODO:インスタントコード public new void Paste() { try { Paste(Clipboard.GetText()); } catch (Exception) { // しらね } } public new void Paste(string text) { try { string test = text; foreach (char c in test.ToCharArray()) { if (this.m_keyEventMap.ContainsKey((Keys)c)) { this.m_keyEventMap[(Keys)c]((Keys)c); } } } catch (Exception) { // しらね } } #endregion }
解説
- 負荷が高くてはてなさんが実は嫌な顔していたら、スマン。
- コメント書いてない辺りだと、TextBox継承してるのでTextBox関係のプロパティ(ShortcutsEnabledとかね)をいじられたらサックリ死にます。
- keyEventMapとかはなんも考えてない。素直にIf文分岐にしろよという声が聞こえてきそう。
- 今回初めてTextBoxの継承ってのを試してみたが、そんなに悪くないかも。
- SelectionLengthをいじってないので使いづらい
- マウスでカーソル位置を調節出来ないので使いづらい
- IME関係は試してない
以上。
誰か素敵コードにしてくれないかなぁ、なんて言ってみたり・・。