前回の
gogowaten.hatenablog.com
これに
gogowaten.hatenablog.com
このときのを取り入れて
- TextBoxに数字の入力中にもMyValueに反映するようにした
- 書式の設定の入力中にもMyTextに反映するようにした
- TextBoxに入力中はMyValueの値をそのまま表示
- TextBoxからフォーカスが離れたら書式を適用
プロパティ名 | 型 | 既定値 | 説明 |
MyValue | decimal | 0m | 値 |
MyText | String | "" | MyValueを書式に従ってToStringしたもの、表示用 |
MyMinValue | decimal | decimal.MinValue | 最小値 |
MyMaxValue | decimal | decimal.MaxValue | 最大値 |
MySmallChange | decimal | 1m | 小変更値、ボタン押したときの変化値 |
MyLargeChange | decimal | 10m | 大変更値、ボタンの上でホイール回したときの変化値 |
MyStringFormat | String | "" | 書式、MyValue.ToString(書式)で使う |
userControl/ControlLibraryCore20200620 at 0703_2312_blog · gogowaten/userControl
MyValueは内部の生の値:decimal型
MyTextは表示用の値:String型
どちらも依存関係プロパティで、値が変更されたときだけ実行されるPropertyChangedCallbackを設定している。
前回までは
MyValueのCallbackでの処理内容は
MyText = 新しい値.ToString(書式)
MyTextのCallbackでの処理内容は
MyValue = decimal.Parse(新しい値)
書式が0.0のときMyValueが1.52だと、MyTextは1.5表記
ここでMyValueが2.52に変更される(処理の流れ1)と、MyValueのCallbackが実行されて、2.52に書式設定された"2.5"がMyTextに入力される(2)
MyTextが"1.5"から"2.5"に変わったので、MyTextのCallbackが実行されて、"2.5"をdecimal型に変換した2.5がMyValueに入力される(3)
MyValueが2.52から2.5に変わったので、MyValueのCallbackが実行されて、2.5に書式設定された"2.5"がMyTextに入力される(4)
MyTextは"2.5"から"2.5"で変化なしなのでCallbackは実行されない
これで処理完了になる。PropertyChangedCallbackは値が変化したときだけ実行されるおかげで、無限ループしなくて済んでいるけど、結果を見ると2.52が入力されたMyValueが2.5になってしまう。
今回の
MyValueのCallbackでの処理内容
MyText = 新しい値.ToString(書式)
MyValue = 新しい値
MyTextのCallbackでの処理内容
MyValue = decimal.Parse(新しい値)
赤字のところを付け足した
処理の流れ
6番目以降が付け足した赤字部分の処理になる
結果は期待通り
これで-0.とか入力できるようになって、内部の値MyValueと表示用の値MyTextをそれぞれ設定できるようになった
数値を文字列に変換するときの書式設定
これも入力中にリアルタイムに反映するようにしたかったので、String型の依存関係プロパティを追加した
名前はMyStringFormat
//書式指定用の文字列型依存関係プロパティ public string MyStringFormat { get { return (string)GetValue(MyStringFormatProperty); } set { SetValue(MyStringFormatProperty, value); } } public static readonly DependencyProperty MyStringFormatProperty = DependencyProperty.Register(nameof(MyStringFormat), typeof(string), typeof(NumericUpDown), new PropertyMetadata("", OnMyStrinfFormatChanged, CoerceMyStrinfFormatValue)); private static void OnMyStrinfFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ud = d as NumericUpDown; var sf = (string)e.NewValue; decimal m = ud.MyValue; ud.MyText = ud.MyValue.ToString(sf); ud.MyValue = m; } //新しい書式の判定、不適切な場合は古い書式で上書きする private static object CoerceMyStrinfFormatValue(DependencyObject d, object baseValue) { var ud = d as NumericUpDown; var format = (string)baseValue;//新しい書式 //正の値、負の値、0のときで書式を変えられる //セミコロンで区切る //(正の値書式 ; 負の値書式 ; 0の書式) //無限ループになる書式は数値に変換できる、かつ // ┣ 0以外 // ┣ 「,.」が含まれている // ┣ 正の値の書式の先頭がハイフン // ┗ 負の値の書式の先頭がハイフンではない //MyValueとMyTextのCallback間で無限ループになってStackOverflowになってしまう //正、負、0、それぞれを判定するため「;」で文字列を分割 var neko = format.Split(";"); for (int i = 0; i < neko.Length; i++) { string sf = neko[i]; if (decimal.TryParse(sf, out decimal m)) { if (Math.Abs(m) != 0 || sf.Contains(",.")) { return ud.MyStringFormat; } if (i == 0 && sf.StartsWith("-")) { return ud.MyStringFormat; } if(i==1 && sf.StartsWith("-") == false) { return ud.MyStringFormat; } } try { //新しい書式適用してみてエラーならcatchに飛ぶ var text = ud.MyValue.ToString(format); //正の値が負の値に変化するような書式も弾く decimal dc = 1m; if (decimal.TryParse(dc.ToString(format), out decimal dd)) { //「-.」や「-,.」などは「-」と同じ効果で、正の値が負の値に反転する if (dd == -1m) { return ud.MyStringFormat; } } } catch (Exception) { return ud.MyStringFormat; } } return format; }
PropertyChangedCallbackでの処理は236行目
240行目
今のMyValueを確保してから
242行目
新しい書式をMyValueに適用して、MyTextに入れて
243行目
確保しておいたMyValueをMyValueに入れる
これは、さっきのMyValueのCallbackで付け足した処理と同じことで、こうしておかないとMyTextを変更したときのMyTextのCallbackでMyValueが変な値になってしまう
Propertyに値が入る直前に必ず実行されるCoerceValueCallback
長い
ToString()で指定できる書式設定はかなり柔軟?で123って数値に
書式 結果
"0" "123"
"0." "123"
"0," "0"
"0,." "0"
"50" "5123"
"99088" "9912388"
"a" エラー
"a0" "a123"
";" ""
"-0" "-123"
基本的には0のところに元の数値が入るけど、0になったり、空白になったり、エラーになったりするし他にも変なのがあるかも。数値が変化してしまうようなものはMyTextとMyValueのCallback間で無限ループになるので、そのような書式だった場合は元の古い書式に戻す(上書きする)ようにしている。
わかった範囲での対処療法的な処理だからツギハギだらけになってしまった。もっといい方法があるはずだけど、今回はこうなった
入力値のリアルタイムでの反映をしなければこんなに大変じゃなかったけど、なんかできそう、やっぱできないかもを繰り返してなんとかできた。まだなんかある気もするけどこれでいいかな
関連記事
次回は3日後
gogowaten.hatenablog.com
今回のUserControlを公開してみた
その7は10日後
gogowaten.hatenablog.com