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

## 背景（前回までのあらすじ）
[doubleとバインドしているTextBoxに小数点が入力できない！](https://qiita.com/h084/items/b562526c3dceb693bd64 "前々回")
[doubleとバインドしているTextBoxに小数点が入力できない！【解決編⁉】](https://qiita.com/h084/items/066d0289833cad858f9b "前回")

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

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

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

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

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

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

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

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

## 動作している様子
![test2.gif](https://qiita-image-store.s3.amazonaws.com/0/293884/72cb6664-0cfe-14db-3e9e-964b3908646b.gif)

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

## コード
```xml:XAML
<TextBox Text="{Binding Path=Number,
                        Mode=TwoWay,
                        UpdateSourceTrigger=PropertyChanged,
                        Converter={StaticResource converter},
                        ConverterParameter='#,0.#####'}" />
```
書式指定を、コンバーターへの引数`ConverterParameter`で渡しています。

```csharp:コンバーター・クラス
//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)
            {
              //入力文字列に小数部があれば、「整数部」＋「ピリオド」＋「入力文字列の小数の桁数分の０」をフォーマットにする
              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`ではないとか、書式に`G`や`F`など標準の書式指定子が指定されたらどうするかとか、いろいろツッコミどころはあると思います！

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