2012-06-27 9 views
15

Estoy intentando vincular TextBox a double propiedad de algún objeto con UpdateSourceTrigger=PropertyChanged. El objetivo es inmediatamente durante la edición, validar que el valor ingresado esté dentro del rango permitido (y mostrar un error si no es así). Deseo implementar la validación en el nivel del modelo, es decir, a través del IDataErrorInfo.Enlace al campo doble con validación

Todo funciona muy bien cuando enlace a la propiedad int, pero si la propiedad es doble, aparece un comportamiento de edición frustrante: después de borrar el último dígito significativo en fracción de número, el separador decimal se borra automáticamente (con todos los ceros fraccionarios posibles) . Por ejemplo, después de borrar el dígito '3' del número '12 .03 ', el texto cambia a' 12 'en lugar de '12 .0'.

Por favor, ayuda.

Aquí es el código de ejemplo:

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

Me imagino que esto tiene que ver con el formateo - ya que 12 es equivalente a 12.00, ¿ha intentado utilizar StringFormat en el enlace? – Charleh

+0

Sí, lo he intentado, pero no me gusta cómo funciona la edición. StringFormat es bueno para presentar, pero durante la edición me gustaría evitarlo. – arudoy

Respuesta

1

Try usando StringFormat en tu bi hallazgo:

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

No estoy seguro si ese formato de cadena es incluso justo ya que no he hecho uno por un tiempo, pero es sólo un ejemplo

+0

Gracias por su sugerencia, pero StringFormat no es conveniente (ver mi comentario anterior). De hecho, voy a aplicar StringFormat después de que TextBox pierda el foco, pero mientras tenga foco me gustaría evitar StringFormat – arudoy

5

Intentó formatear el valor con decimales?

Puede ser extraño, ya que siempre tendrá N lugares decimales.

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

Si tener cifras decimales fijos no es lo suficientemente bueno, puede que tenga que escribir un convertidor que trata el valor como una cadena y la convierte de nuevo a un doble.

+0

Sí, lo he intentado. Con la edición habilitada de StringFormat tampoco funciona muy bien. Por ejemplo, con StringFormat, el separador decimal que borra con la tecla de borrar de '12 .00 'lleva a' 1200.00 'con el cursor después de' 12 '. Por lo tanto, es imposible borrar toda la parte fraccional, lo que podría ser bastante sorprendente para el usuario. – arudoy

+0

También intenté escribir mi propio convertidor. La conversión de string a double funciona bien, pero hay un problema con la conversión de respaldo: ¿cómo podría saber el convertidor que debería insertar un separador decimal en la representación de cadena del valor 12? El usuario podría escribir '12' o '12 '. o incluso '12 .0 'y todos tienen el valor 12, que se pasa al convertidor – arudoy

+0

. Esto es exactamente por lo que el comportamiento predeterminado es hacerlo así. Será difícil adivinar cuál debería ser el valor visible si la UI no tiene "estado"; tal vez un control personalizado podría hacer el trabajo:/ –

9

Me doy cuenta de que llego un poco tarde a la fiesta, pero encontré una solución (creo) bastante limpia para este problema.

Un convertidor inteligente que recuerda la última cadena convertida a doble y devuelve que si existe debe hacer todo lo que desee.

Tenga en cuenta que cuando el usuario cambia el contenido del cuadro de texto, ConvertBack almacenará la cadena de la entrada del usuario, analizará la cadena para un doble y pasará ese valor al modelo de vista. Inmediatamente después, se llama a Convert para mostrar el valor recién cambiado. En este punto, la cadena almacenada no es nula y se devolverá.

Si la aplicación en lugar del usuario hace que el doble cambie solo se llama Convertir. Esto significa que la cadena almacenada en caché será nula y se invocará un ToString() estándar en el doble.

De esta manera, el usuario evita sorpresas extrañas al modificar los contenidos del cuadro de texto, pero la aplicación todavía puede provocar un cambio.

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; 
    } 
} 
+0

Esta es una buena solución. Sin embargo, estropeará las cosas al llamar NotifyPropertyChanged al mismo tiempo en más de un TextBoxes utilizando este convertidor. Sugiero pasar la propiedad como un parámetro para el convertidor y reemplazar 'lastConvertBackString' por un' Diccionario '. De esa manera puedes recordar la última cadena para cada propiedad. – Octan

2

se han topado con el mismo problema, y ​​han encontrado una solución bastante simple: utilizar un validador personalizado, que no vuelve "válida" cuando el texto termina en "" o "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; 
} 
+0

Creo que mostrará un error si escribo 100 –

+0

True. Agrega un '' tmp.Contains (',') || tmp.Contains ('.') '' en la comprobación de '' EndsWith (".") '' para evitar esto. – Digifaktur

3

El problema es que está actualizando su propiedad cada vez que cambia el valor. Cuando se cambia a 12,03 12,0 se redondea a 12.

Se puede ver cambios proporcionando delay cambiando el TextBox en xaml como esto

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

pero delay notificará y establezca la propiedad después de que el tiempo de retardo en mili sec. mejor uso StringFormat como esto

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

El comportamiento de los valores de coma flotante se une a un cuadro de texto se ha cambiado de .NET 4 a 4,5. Con .NET 4.5 ya no es posible ingresar un carácter separador (coma o punto) con 'UpdateSourceTrigger = PropertyChanged' de forma predeterminada.

Microsoft dice, esto (es) destinado

Si aún desea utilizar 'UpdateSourceTrigger = PropertyChanged', que puede forzar el comportamiento .NET 4 en el .NET 4,5 aplicación añadiendo el siguiente línea de código al constructor de su App.xaml.cs:

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

(Sebastián Lux - copiado textualmente de here)

Cuestiones relacionadas