【问题标题】:Binding to double field with validation通过验证绑定到双字段
【发布时间】:2012-06-27 09:34:09
【问题描述】:

我正在尝试将TextBox 绑定到具有UpdateSourceTrigger=PropertyChanged 的某个对象的double 属性。目标是在编辑期间立即验证输入的值是否在允许的范围内(如果不是则显示错误)。我想在模型级别实现验证,即通过IDataErrorInfo

当我绑定到 int 属性时,一切都很好,但如果属性是双倍的,则会出现令人沮丧的编辑行为:在擦除数字小数部分的最后一个有效数字后 - 小数分隔符被自动擦除(所有可能的小数零)。例如,从数字“12.03”中删除数字“3”后,文本变为“12”而不是“12.0”。

请帮忙。

这里是示例代码:

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)); 
    }
  }
}

【问题讨论】:

  • 我想这与格式化有关——因为 12 相当于 12.00,您是否尝试过在绑定时使用 StringFormat?
  • 是的,我试过了,但不喜欢使用它进行编辑的方式。 StringFormat 适合呈现,但在编辑过程中我想避免它。

标签: wpf binding


【解决方案1】:

将浮点值绑定到文本框的行为已更改 从 .NET 4 到 4.5。使用 .NET 4.5 不再可能输入 带有 ‘UpdateSourceTrigger = 的分隔符(逗号或点) PropertyChanged' 默认情况下。

微软说,这是(是)有意的

如果您仍想使用“UpdateSourceTrigger = PropertyChanged”,您 可以通过添加来强制 .NET 4.5 应用程序中的 .NET 4 行为 将以下代码行添加到App.xaml.cs 的构造函数中:

public App()  
{
    System.Windows.FrameworkCompatibilityPreferences
               .KeepTextBoxDisplaySynchronizedWithTextProperty = false;   
}

(Sebastian Lux - 从here逐字复制)

【讨论】:

    【解决方案2】:

    我意识到我参加聚会有点晚了,但我找到了一个(我认为)相当干净的解决方案来解决这个问题。

    一个聪明的转换器,它记住最后一个转换为双精度的字符串并返回如果它存在应该做你想做的一切。

    请注意,当用户更改文本框的内容时,ConvertBack 将存储用户输入的字符串,将字符串解析为双精度,并将该值传递给视图模型。紧接着,调用 Convert 来显示新更改的值。此时存储的字符串不为空,将被返回。

    如果应用程序而不是用户导致双精度更改,则仅调用 Convert 。这意味着缓存的字符串将为空,并且将在 double 上调用标准 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;
        }
    }
    

    【讨论】:

    • 这是一个很好的解决方案。但是,当使用此转换器在多个 TextBox 上同时调用 NotifyPropertyChanged 时,它会搞砸。我建议将该属性作为转换器的参数传递,并将lastConvertBackString 替换为Dictionary&lt;String, String&gt;。这样你就可以记住每个属性的最后一个字符串。
    【解决方案3】:

    试过用小数位格式化值吗?

    这可能很奇怪,因为你总是有 N 个小数位。

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

    如果固定小数位不够好,您可能必须编写一个转换器,将值视为字符串并将其转换回双精度值。

    【讨论】:

    • 是的,我试过了。启用 StringFormat 编辑工作也不是很好。例如,使用 StringFormat 删除小数分隔符,从 '12.00' 删除键会导致 '1200.00' 光标在 '12' 之后。因此,不可能擦除整个小数部分,这可能会让用户感到惊讶。
    • 我也尝试过编写自己的转换器。从字符串到双精度的转换可以,但是反向转换有一个问题:转换器怎么知道它应该在值 12 的字符串表示中插入小数分隔符?用户可以输入“12”或“12”。甚至是“12.0”,这些都具有传递给转换器的值 12
    • 这就是为什么默认行为是按它的方式做的。如果 UI 没有“状态”,则很难猜测可见值应该是什么 - 也许自定义控件可以完成工作:/
    • 你知道如何实现这样的控制吗?
    【解决方案4】:

    问题是每次值更改时您都会更新您的属性。当您将 12.03 更改为 12.0 时,它会四舍五入为 12。

    您可以通过像这样更改xaml 中的TextBox 来提供delay 来查看更改

    <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}">
    

    【讨论】:

      【解决方案5】:

      我遇到了同样的问题,并找到了一个非常简单的解决方案:使用自定义验证器,当文本以“。”结尾时,它不会返回“有效”。或“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;
      }
      

      【讨论】:

      • 是的。添加一个 ''tmp.Contains(',') || tmp.Contains('.')'' 在检查 ''EndsWith(".")'' 以避免这种情况。
      【解决方案6】:

      尝试在您的绑定上使用 StringFormat:

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

      不确定该字符串格式是否正确,因为我已经有一段时间没有做过了,但这只是一个示例

      【讨论】:

      • 感谢您的建议,但 StringFormat 不方便(请参阅我上面的评论)。如果事实上,我将在 TextBox 失去焦点之后应用 StringFormat ,但是当它有焦点时,我想避免 StringFormat
      猜你喜欢
      • 1970-01-01
      • 2011-01-19
      • 1970-01-01
      • 1970-01-01
      • 2016-07-05
      • 1970-01-01
      • 2011-08-03
      • 1970-01-01
      • 2017-07-25
      相关资源
      最近更新 更多