2012-06-20 21 views
7

Al usar textbox.Undo(); Me sale el siguiente error:No se puede usar Deshacer en TextChanged

Cannot Undo or Redo while undo unit is open.

Ahora entiendo por qué este es el caso (ya que es un evento deshacer activo) sino a través de qué evento puedo realizar la validación en un cuadro de texto y deshacer el cambio si el usuario tiene escribió un caracter inválido?

+0

Debería usar comportamientos en su lugar. – SepehrM

+0

SepehrM: ¿podría dar un ejemplo? ¿Qué sucede si aún NO quiere validar, pero solo quiere evitar que este maldito mensaje de excepción arruine su sesión? Estoy obteniendo esto cuando un usuario escribe un poquito demasiado rápido o tal vez pega una porción de datos en el campo. Ni siquiera validando. – Allen

Respuesta

6

En lugar de usar Deshacer y Cambiar de texto, debe usar los eventos PreviewTextInput y DataObject.Pasting. En el controlador de eventos PreviewTextInput, establezca e.Handled en true si el texto escrito es un carácter no válido. En el controlador de eventos Pasting, llame al e.CancelCommand() si el texto pegado no es válido.

Aquí se muestra un ejemplo de un cuadro de texto que acepta sólo los dígitos 0 y 1:

XAML:

<Window x:Class="BinaryTextBox.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="133" Width="329"> 
    <StackPanel> 
     <TextBox x:Name="txtBinary" Width="100" Height="24" 
       PreviewTextInput="txtBinary_PreviewTextInput" 
       DataObject.Pasting="txtBinary_Pasting"/> 
    </StackPanel> 
</Window> 

Código atrás:

using System.Text.RegularExpressions; 
using System.Windows; 
using System.Windows.Input; 

namespace BinaryTextBox 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     private void txtBinary_PreviewTextInput(object sender, 
       TextCompositionEventArgs e) 
     { 
      e.Handled = e.Text != "0" && e.Text != "1"; 
     } 

     private void txtBinary_Pasting(object sender, DataObjectPastingEventArgs e) 
     { 
      if (!Regex.IsMatch(e.DataObject.GetData(typeof(string)).ToString(), "^[01]+$")) 
      { 
       e.CancelCommand(); 
      } 
     } 
    } 
} 
+0

Brillante - solución perfecta gracias –

+0

Genial, el controlador "pegar" también valida si está insertando caracteres usando arrastrar y soltar. – Mishax

+0

El problema con este enfoque es que PreviewTextInput solo previsualiza lo que se escribe y no el resultado final.Esto solo puede funcionar si se escribe un algoritmo muy complicado e inmanejable para tener en cuenta todas las entradas posibles con respecto a un valor válido. Por ejemplo, en un campo decimal, intente validar cuándo es aplicable un signo menos o un período. Claramente, solo puede escribir 1 de cada uno, pero no en cada lugar. La mejor solución es validar el resultado final, sin consideración de cómo se tipeó. WPF no tiene soporte para eso, desafortunadamente. – Neolisk

3

Llame al deshacer de forma asíncrona desde el Desde el controlador de eventos TextChanged:

Dispatcher.BeginInvoke(new Action(() => tb.Undo())) 
+0

¿Cómo se compara esta solución con la respuesta aceptada? – InvalidBrainException

7

Para responder al enfoque de simbay, que creo que se descarta.

No puede llamar a Deshacer en TextChanged porque la operación de deshacer todavía está siendo preparada por el TextBox. Parece funcionar a veces y no en otras ocasiones, por lo que esto sugiere que hay una condición de carrera entre cuando se señala el evento y la finalización de la preparación de deshacer.

Sin embargo, al invocar Deshacer invocado en el Dispatcher, el cuadro de texto completará su preparación para deshacer. Puede validar los resultados del cambio de texto y luego decidir si desea mantener o deshacer el cambio. Puede que este no sea el mejor enfoque, pero lo intenté y destruí un montón de cambios de texto y pegué en el cuadro de texto y no pude reproducir la excepción.

La "respuesta aceptada" es excelente SÓLO si desea evitar que se ingrese o pegue un carácter no válido, pero en general a menudo valgo mucho más la validación de la entrada de TextBox y quiero verificar el valor del texto final. No es fácil discernir el texto final de un evento de vista previa porque, en lo que respecta al control, nada ha sucedido todavía.

Para responder a la pregunta de Terribad, la respuesta de Simbay es mejor y más concisa en más situaciones.

tb.TextChanged =+ (sender, args) => 
{ 
    if(! MeetsMyExpectations(tb.Text)) 
     Dispatcher.BeginInvoke(new Action(() => tb.Undo())); 
} 

He leído un montón de aventuras salvajes en la validación de cuadro de texto y esto es casi tan fácil como lo he encontrado.

Cuestiones relacionadas