Help us understand the problem. What is going on with this article?

doubleとバインドしているTextBoxに小数点が入力できない!【完結編‼】

More than 1 year has passed since last update.

背景(前回までのあらすじ)

doubleとバインドしているTextBoxに小数点が入力できない!
doubleとバインドしているTextBoxに小数点が入力できない!【解決編⁉】

  • C# + XAML
  • TextBoxが数値型のプロパティとバインドしている
  • リアルタイムに入力値でバインドしているプロパティを更新する

このままでは小数部の入力に問題があります。そこで

  • コンバーターを付けた
    • 前回と違う値なら、書式指定を適用してTextBoxに書き戻す
    • 前回と同じ値なら、入力文字列をそのままTextBoxに書き戻す

としましたが、次のような場合、やはりうまくいきません!

  • 整数部の3桁ごとではないところへカンマが入力できてしまう
  • 小数点以下の数字を編集し、末尾が小数点や0になった場合、これらが消えてしまう

コメントをいただきました!

またまた、@albireoさんにコメントをいただきました!ありがとうございます!

書式指定を動的に変更!

そこでちょっと発想を変えて、入力文字列をそのまま戻そうとするのではなく、末尾の小数点や0が出るように、入力文字列に合わせて書式指定を変更することにしました。

  • 基本的には、もともと指定された書式指定を適用する
    • これで、ヘンなところに入力されたカンマは除去できます
  • 入力文字列の最後が小数点なら、もともとの書式指定の整数部の後ろに小数点の記号を付けた書式指定を適用する
    • これで、末尾の小数点を出力できます
  • 入力文字列に小数部があるなら、もともとの書式指定の小数部を、少なくとも入力文字列の小数部の桁を出力する書式指定に変えて適用する
    • これで、小数点以下の末尾の0を出力できます

動作している様子

test2.gif

入力につれて、カンマが適切な位置にあることに注目!

コード

XAML
<TextBox Text="{Binding Path=Number,
                        Mode=TwoWay,
                        UpdateSourceTrigger=PropertyChanged,
                        Converter={StaticResource converter},
                        ConverterParameter='#,0.#####'}" />

書式指定を、コンバーターへの引数ConverterParameterで渡しています。

コンバーター・クラス
//doubleと文字列のコンバーター
public class DoubleToStringConverter : System.Windows.Data.IValueConverter
{
  private string convertBackString; //入力文字列

  //doubleから文字列への変換(表示時に使われる)
  public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    try
    {
      if (!(value is double)) return null;

      string format = (parameter as string) ?? string.Empty;
      if (!string.IsNullOrEmpty(convertBackString))
      {
        //フォーマットを「整数部」と「小数部」に分ける
        var formatParts = format.Split('.');
        if (formatParts.Length >= 2)
        {
          //入力文字列が小数点で終わっていたら、「整数部」+「小数点そのもの」をフォーマットにする
          if (convertBackString.EndsWith(culture.NumberFormat.NumberDecimalSeparator))
          {
            format = formatParts[0] + @"\" + culture.NumberFormat.NumberDecimalSeparator;
          }
          else
          {
            var pos = convertBackString.IndexOf(culture.NumberFormat.NumberDecimalSeparator);
            if (pos >= 0)
            {
              //入力文字列に小数部があれば、「整数部」+「ピリオド」+「入力文字列の小数の桁数分の0」をフォーマットにする
              var digitLength = convertBackString.Length - pos - 1;
              format = formatParts[0] + @"."
                  + new string('0', digitLength)
                  + formatParts[1].Substring(Math.Min(digitLength, formatParts[1].Length));
            }
          }
        }
      }
      return ((double)value).ToString(format);
    }
    finally
    {
      convertBackString = null;
    }
  }

  //文字列からdoubleへの変換(入力時に使われる)
  public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    //入力文字列を保存する
    convertBackString = value as string;

    if (!string.IsNullOrEmpty(convertBackString)
      && double.TryParse(convertBackString, out double newValue))
    {
      //入力文字列をパーズして戻す
      return newValue;
    }
    else
    {
      return null;
    }
  }
}

コンバーターが内部状態convertBackStringを持っていますから、同じコンバーターのインスタンスを複数のTextBoxで使いまわすと、マズいかもしれません。

小数点をどのように表現するかはロケールによって違います。CultureInfo.NumberFormat.NumberDecimalSeparatorが小数点の記号ですね。

完結!

一応、完成としておきます!

書式指定を記述するのがStringFormatではないとか、書式にGFなど標準の書式指定子が指定されたらどうするかとか、いろいろツッコミどころはあると思います!

改善案などがあれば、ぜひ、コメントしてください!よろしくお願いします!

h084
C#やWPF周辺について書いていこうと考えています!
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした