スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: スポンサー広告

数値型プロパティとTextBox.Textプロパティとのバインド

数値のみ入力を許可する用なTextBoxの場合、TextBoxのTextプロパティと数値型プロパティのバインドを行う。そうしないと、BindingのStringFormatが有効にならない。でも、そうした場合、入力が空になった時に問題が発生した。
例えば、TextBoxVMを改造して数値ボックス用のViewModelを作ったとする。
using System;

namespace TawamureDays.ViewModels {

/// <summary>
/// 数値用TextBox用ViewModelクラス
/// </summary>
public class NumericBoxVM : UIElementVM {

#region コンストラクタ

/// <summary>
/// コンストラクタ
/// </summary>
public NumericBoxVM() : base() {
}

/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="inputValue">初期値</param>
public NumericBoxVM(decimal? inputValue) : this() {
inputValue_ = inputValue;
}

#endregion

#region プロパティ

/// <summary>入力値</summary>
private decimal? inputValue_;

/// <summary>
/// 入力値を取得|設定します。
/// </summary>
public decimal? InputValue {
get {return inputValue_;}
set {
if (inputValue_ != value) {
inputValue_ = value;
this.NotifyPropertyChanged("InputValue");
}
}
}

#endregion
}
}
InputValueのプロパティ型がdecimal?なのは、空文字列をnulとして受け取りたい為。
これを使ってみる。
○ViewModel側
using System.Windows;
using System.Windows.Input;

namespace TawamureDays {

/// <summary>
/// ViewModelクラス
/// </summary>
public class TawamureVM : BaseWindowVM {

/// <summary>
/// 数値入力用VMを取得します。
/// </summary>
public NumericBoxVM NumericBoxVM {
get; private set;
}

/// <summary>
/// コンストラクタ
/// </summary>
public TawamureVM() {
closeCommand_ = new RelayCommand(ExecuteCloseCommand);
NumericBoxVM = new NumericBoxVM();
return;
}
}
}

○View側
<Window x:Class="TawamureDays.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tw="http://schemas.tawamuredays.jp/wpf/gui"
Title="MainWindow" Height="350" Width="525">
<tw:TawamureContents>
<StackPanel>
<TextBox DataContext="{Binding NumericBoxVM}"
Text="{Binding InputValue}"
/>
</StackPanel>
<tw:TawamureContents.Footer>
<Button Content="終われ!" Command="{Binding CloseCommand}"/>
</tw:TawamureContents.Footer>
</tw:TawamureContents>
</Window>
そうした場合、起動は特に問題ない。問題は入力の時に出てくる。
数値を入力する分には問題ない。問題は、数値を入力した後で、文字列をクリアした時に発生する。
数値を入力する。
20120915_1
その後、空にすると…
20120915_2
バインディングエラーを示す赤色になる。裏で発生しているエラーが下のようになる。

System.Windows.Data Error: 7 : ConvertBack cannot convert value '' (type 'String'). BindingExpression:Path=InputValue; DataItem='NumericBoxVM' (HashCode=13635742); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String') FormatException:'System.FormatException: 入力文字列の形式が正しくありません。
場所 System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
場所 System.Number.ParseDecimal(String value, NumberStyles options, NumberFormatInfo numfmt)
場所 System.Convert.ToDecimal(String value, IFormatProvider provider)
場所 System.String.System.IConvertible.ToDecimal(IFormatProvider provider)
場所 System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
場所 MS.Internal.Data.SystemConvertConverter.ConvertBack(Object o, Type type, Object parameter, CultureInfo culture)
場所 System.Windows.Data.BindingExpression.ConvertBackHelper(IValueConverter converter, Object value, Type sourceType, Object parameter, CultureInfo culture)'

どうも、空文字列をdecimal?のnullに変換できないらしい。まあ、decimal?系のnullは、クラス等のnullとは違うようなので、当たり前といえば当たり前の話だった。
じゃあ、どうやれば、変換できるのか?といえば、これはもうConverterを使うしかない。

using System;

namespace TawamureDays.Converters {

/// <summary>
/// 数値型(decimal?)を文字列に変えるコンバータクラス
/// </summary>
[System.Windows.Data.ValueConversion(typeof(decimal?), typeof(string))]
public sealed class NullableDecimal2StringConverter : System.Windows.Data.IValueConverter {

#region IValueConverter メンバー

/// <summary>
/// 値を変換します。
/// </summary>
/// <param name="value">バインディング ソースによって生成された値。</param>
/// <param name="targetType">バインディング ターゲット プロパティの型。</param>
/// <param name="parameter">使用するコンバーター パラメーター</param>
/// <param name="culture">コンバーターで使用するカルチャ。</param>
/// <returns>変換された値</returns>
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if (value == null) {
return string.Empty;
}

decimal decVal;

if (!decimal.TryParse(value.ToString(), out decVal)) {
return value;
}

var format = parameter == null ? string.Empty : parameter.ToString();
return decVal.ToString(format);
}

/// <summary>
/// 値を変換します。
/// </summary>
/// <param name="value">バインディング ターゲットによって生成される値。</param>
/// <param name="targetType">変換後の型。</param>
/// <param name="parameter">使用するコンバーター パラメーター</param>
/// <param name="culture">コンバーターで使用するカルチャ。</param>
/// <returns>変換された値</returns>
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if (value == null) {
return (decimal?)null;
}

decimal decVal = decimal.Zero;

if (decimal.TryParse(value.ToString(), out decVal)) {
return decVal;
}

return (decimal?)null;
}

#endregion
}
}
ソース→ターゲットは、decimal?型をstring型に、ターゲット→ソースは、string型をdecimalにパースしている。decimal型に変換できない場合にdecimal?のnullを返している。
これで、空文字列でもバインドエラーが言われなくなった。ただ、このコンバータを噛ましてしまうと、StringFormatが効かなくなる。まあ、これはどうしようもないので、ConverterParameterに書式を設定するとみなして、変換するしかない。何もかもが上手くいくものではないか。

(2013/2/20追記)
数値だけのTextBoxだってやれるはずだ(壱)。も、数値入力専用TextBoxの話になっている。
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0


この記事へのコメント

コメントの投稿

非公開コメント


サイドバー背後固定表示サンプル

当ブログに書かれたソースコードは流用自由です。

バグ、スペルミス等はありうる事です。

ご利用の際は自己責任でお願いしますm(_ _)m

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。