17

でいくつかのオブジェクトのプロパティにバインドTextBoxしようとしています。目標は、入力された値が許容範囲内であることを編集中にすぐに検証することです (そうでない場合はエラーを表示します)。モデル レベルで検証を実装したい、つまり.doubleUpdateSourceTrigger=PropertyChangedIDataErrorInfo

int プロパティにバインドするとすべてがうまく機能しますが、プロパティが double の場合、イライラする編集動作が表示されます: 数値の小数部分の最後の有効数字を消去した後、小数点記号は自動的に消去されます (可能なすべての小数ゼロ)。たとえば、数字「12.03」から数字「3」を消去すると、テキストは「12.0」ではなく「12」に変更されます。

助けてください。

サンプルコードは次のとおりです。

MainWindow.xaml:

<Window x:Class="BindWithValidation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="80" Width="200" WindowStartupLocation="CenterOwner">

  <StackPanel>
    <TextBox Width="100" Margin="10" Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
      <TextBox.Style>
        <Style TargetType="TextBox">
          <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="true">
              <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
            </Trigger>
          </Style.Triggers>
        </Style>
      </TextBox.Style>
    </TextBox>
  </StackPanel>
</Window>

MainWindow.xaml.cs:

namespace BindWithValidation
{
  public partial class MainWindow : Window
  {
    private UISimpleData _uiData = new UISimpleData();

    public MainWindow()
    {
      InitializeComponent();
      DataContext = _uiData;
    }
  }
}

UISimpleData.cs:

namespace BindWithValidation
{
  public class UISimpleData : INotifyPropertyChanged, IDataErrorInfo
  {
    private double _doubleField = 12.03;

    public double DoubleField
    {
      get
      {
        return _doubleField;
      }
      set
      {
        if (_doubleField == value)
          return;

        _doubleField = value;
        RaisePropertyChanged("DoubleField");
      }
    }

    public string this[string propertyName]
    {
      get
      {
        string validationResult = null;
        switch (propertyName)
        {
          case "DoubleField":
          {
            if (DoubleField < 2 || DoubleField > 5)
              validationResult = "DoubleField is out of range";
            break;
          }

          default:
            throw new ApplicationException("Unknown Property being validated on UIData");
        }

        return validationResult;
      }
    }

    public string Error { get { return "not implemented"; } }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged(string property)
    {
      if ( PropertyChanged != null )
        PropertyChanged(this, new PropertyChangedEventArgs(property)); 
    }
  }
}
4

6 に答える 6

10

私はパーティーに少し遅れていることに気づきましたが、この問題に対するかなりクリーンな解決策を見つけました (と思います)。

double に変換された最後の文字列を記憶し、それが存在する場合は必要なすべてを実行することを返す賢いコンバーター。

ユーザーがテキスト ボックスの内容を変更すると、ConvertBack はユーザーが入力した文字列を格納し、文字列を解析して double を検出し、その値をビュー モデルに渡します。その直後に Convert が呼び出され、新しく変更された値が表示されます。この時点で、格納された文字列は null ではなく、返されます。

ユーザーではなくアプリケーションが double を変更する場合は、Convert のみが呼び出されます。これは、キャッシュされた文字列が null になり、標準の ToString() が倍精度で呼び出されることを意味します。

このようにして、ユーザーはテキストボックスの内容を変更するときに奇妙な驚きを回避できますが、アプリケーションは変更をトリガーできます。

public class DoubleToPersistantStringConverter : IValueConverter
{
    private string lastConvertBackString;

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (!(value is double)) return null;

        var stringValue = lastConvertBackString ?? value.ToString();
        lastConvertBackString = null;

        return stringValue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (!(value is string)) return null;

        double result;
        if (double.TryParse((string)value, out result))
        {
            lastConvertBackString = (string)value;
            return result;
        }

        return null;
    }
}
于 2013-02-12T14:01:09.570 に答える
4

小数点以下の桁数で値をフォーマットしようとしましたか?

ただし、常に小数点以下の桁数が N であるため、奇妙かもしれません。

<TextBox.Text>
    <Binding Path="DoubleField" StringFormat="{}{0:0.00}" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True"/>
</TextBox.Text>

小数点以下の桁数を固定するだけでは不十分な場合は、値を文字列として扱い、double に変換するコンバーターを作成する必要がある場合があります。

于 2012-06-27T09:57:04.043 に答える
4

問題は、値が変更されるたびにプロパティを更新していることです。12.03 を 12.0 に変更すると、12 に丸められます。

このようにinを変更して提供delayすることで、変更を確認できますTextBoxxaml

<TextBox Width="100" Margin="10" Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged,Delay=500, ValidatesOnDataErrors=True}">

ただしdelay、ミリ秒単位の遅延時間後にプロパティを通知および設定します。StringFormatこのように使用する方が良い

<TextBox Width="100" Margin="10" Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged,StringFormat=N2, ValidatesOnDataErrors=True}">
于 2015-06-30T10:12:36.457 に答える
2

私は同じ問題に遭遇し、非常に簡単な解決策を見つけました。テキストが「。」で終わる場合に「有効」を返さないカスタムバリデーターを使用します。または「0」:

double val = 0;
string tmp = value.ToString();

if (tmp.EndsWith(",") || tmp.EndsWith("0") || tmp.EndsWith("."))
{
    return new ValidationResult(false, "Enter another digit, or delete the last one.");
}
else
{
    return ValidationResult.ValidResult;
}
于 2015-01-08T11:19:46.240 に答える
1

バインディングで StringFormat を使用してみてください。

<TextBox Width="100" Margin="10" Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, StringFormat='0.0'}"> 

しばらく行っていないため、その文字列形式が正しいかどうかはわかりませんが、これは単なる例です

于 2012-06-27T09:40:36.517 に答える