2011-01-04 24 views
10

Acabo de actualizar nuestra aplicación wpf de 3.5sp1 a 4.0.WPF TextBox Enlace con formato

El siguiente código usamos para vincular el cuadro de texto al modelo de vista subyacente. El cuadro de texto es editable.

<TextBox HorizontalContentAlignment="Right" 
Text="{Binding Path=Price, StringFormat={0:#,##0;(#,##0)}, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}"/> 

En 3.5sp1 el formateo solo ocurriría inicialmente. Por lo tanto, cuando el cuadro de texto se cargó y se vinculó al valor 4000, el formato lo cambiaría a 4.000. Si el usuario edita este valor, no se formateará.

En 4.0, el formateo ocurre cuando el valor cambia (es decir, mientras el usuario ingresa un nuevo valor). Si bien en teoría esto suena bien, en realidad es un desastre. El cursor está por todos lados. Es inutilizable.

Ahora, podríamos cambiar UpdateSourceTrigger a "LostFocus" pero eso introduce nuevos problemas con datos que no se validan en ciertos escenarios.

¿Hay alguna manera de recuperar el antiguo comportamiento 3.5sp1?

Actualización 1

Utilizando Convertidor todavía procudes mismo comportamiento:

public class DecimalConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     if (value != null) 
      return ((decimal)value).ToString("#,##0;(#,##0)"); 

     return string.Empty; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     return value; 
    } 
} 

y el XAML modificación:

<TextBox Text="{Binding Path=Price, Converter={StaticResource DecimalConverter}, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}"/> 

Actualización 2

Similar a este connect article.

+0

Duro. Quizás la mejor ruta es examinar los problemas específicos que tiene con LostFocus. Por ejemplo, tuve que activar manualmente la vinculación/validación cuando el usuario presionó Control-S mientras estaba dentro del cuadro de texto. –

+0

Sí, Jonathan, comencé a mirar LostFocus, pero esperaba evitar un cambio global masivo. :-( – ozczecho

Respuesta

1

Como una actualización, tomé la sugerencia de Jonathans y rejicé el Enlace para usar LostFocus en lugar de PropertyChanged (donde sea apropiado, es decir, dondequiera que se haya especificado StringFormat).

Como dijo Jonathan, en algunos casos debe activar la actualización/validación de enlace manualmente teniendo en cuenta este enfoque.

Si alguien tiene un mejor enfoque, me encantaría verlo.

-1

Puede intentar eliminar StringFormat={0:#,##0;(#,##0)} y escribir el convertidor para formatear.

+0

¿Cómo podría ayudar? Tengo la impresión de que la clase de enlace todavía controlaría cuando se activa el convertidor. –

+0

no se puede hacer nada en la función de conversión del convertidor –

+1

Estaba pensando en ir por la ruta del convertidor, pero quería comprobar si había otra forma. Después de su respuesta, decidí probarlo, pero el comportamiento es el mismo (ver actualización 1). – ozczecho

0

No estaba satisfecho con la solución LostFocus, por lo que decidí codificar un método que moviera manualmente el cursor correctamente. Lo puse en el código detrás del archivo y al agregarlo al evento TextChanged en el TextBox hace que se ejecute cada vez que cambia el texto.

void moveCaret(object sender, TextChangedEventArgs args) 
{ 
    TextBox tb = (TextBox) sender; 
    if (args.Changes.Any()) 
    { 
     var first = args.Changes.First(); 
     int offset = 1; 
     if(first.AddedLength > 0) 
     { 
      if (tb.Text.Length > 4 && tb.Text.Length % 4 == 1) 
       offset = 2; 
      tb.CaretIndex = first.Offset + offset; 
     } 
     else 
     { 
      if (tb.CaretIndex > 0) 
      { 
       offset = 0; 
       if (tb.Text.Length > 2 && (tb.Text.Length + 2) % 4 == 1) 
        offset = -1; 
       tb.CaretIndex = first.Offset + offset; 
      } 
     } 
    } 
    args.Handled = true; 
} 

Sólo tiene que añadir esto al evento TextChanged así:

MyTextBox.TextChanged += moveCaret; 

No estoy 100% seguro, pero esto parece comportarse bien, a pesar de que no se ocupa de la supresión del separador de miles .

EDIT: He descubierto cómo manejar el separador de miles. Hice otro método en el código detrás del archivo y lo coloqué en el evento PreviewKeyDown en el TextBox. Este método comprueba si el TextBox está recibiendo una entrada de Retroceso de botón Eliminar, y simplemente lo ignora y mueve el símbolo de intercalación en lugar.

private void handleThousandSeparator(object sender, KeyEventArgs e) 
{ 
    var textBox = sender as TextBox; 
    if (e.Key == Key.Back) 
    { 
     if (textBox.CaretIndex > 0) 
     { 
      if (textBox.Text[textBox.CaretIndex - 1] +"" == System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator) 
      { 
       if (textBox.Text[0] + "" == System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator) 
        return; 
       textBox.CaretIndex = textBox.CaretIndex - 1; 
       e.Handled = true; 
      } 
     } 
    } 
    if (e.Key == Key.Delete) 
    { 
     if (textBox.CaretIndex < textBox.Text.Length) 
     { 
      if (textBox.Text[textBox.CaretIndex] + "" == System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator) 
      { 
       if (textBox.Text[0] + "" == System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator) 
        return; 
       textBox.CaretIndex = textBox.CaretIndex + 1; 
       e.Handled = true; 
      } 
     } 
    } 
}  

Aviso el caso especial de un separador de miles en el primer carácter en el cuadro de texto, donde se elimina en lugar de omitir. Idealmente, un separador de mil nunca debería estar allí, pero el formateador de número n0 no maneja el caso en el que elimina los primeros números antes del primer mil separador.