Pergunta

Eu estou tentando validar o formulário WPF contra um objeto. Os incêndios de validação quando eu digitar algo no foco perder caixa de texto voltar para a caixa de texto e, em seguida, apagar o que eu escrevi. Mas se eu simplesmente carregar o aplicativo WPF e guia para fora da caixa de texto sem escrever e apagar qualquer coisa de caixa de texto, então não é demitido.

Aqui é a classe Customer.cs:

public class Customer : IDataErrorInfo
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public string Error
        {
            get { throw new NotImplementedException(); }
        }
        public string this[string columnName]
        {
            get
            {
                string result = null;

                if (columnName.Equals("FirstName"))
                {
                    if (String.IsNullOrEmpty(FirstName))
                    {
                        result = "FirstName cannot be null or empty"; 
                    }
                }
                else if (columnName.Equals("LastName"))
                {
                    if (String.IsNullOrEmpty(LastName))
                    {
                        result = "LastName cannot be null or empty"; 
                    }
                }
                return result;
            }
        }
    }

E aqui está o código WPF:

<TextBlock Grid.Row="1" Margin="10" Grid.Column="0">LastName</TextBlock>
<TextBox Style="{StaticResource textBoxStyle}" Name="txtLastName" Margin="10"
         VerticalAlignment="Top" Grid.Row="1" Grid.Column="1">
    <Binding Source="{StaticResource CustomerKey}" Path="LastName"
             ValidatesOnExceptions="True" ValidatesOnDataErrors="True"
             UpdateSourceTrigger="LostFocus"/>         
</TextBox>
Foi útil?

Solução

Se você não é adverso a colocar um pouco de lógica em seu atrás de código, você pode lidar com o real LostFocus evento com algo como isto:

XAML

<TextBox LostFocus="TextBox_LostFocus" ....

.xaml.cs

private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
     ((Control)sender).GetBindingExpression(TextBox.TextProperty).UpdateSource();
}

Outras dicas

Infelizmente isso é por design. validação de WPF só é acionado se o valor no controle mudou.

Inacreditável, mas verdadeiro. Até agora, a validação WPF é a dor proverbial grande - é terrível.

Uma das coisas que você pode fazer, porém, é obter a expressão de vinculação de propriedade do controle e manualmente invocar as validações. É chato, mas funciona.

Dê uma olhada na ValidatesOnTargetUpdated propriedade de ValidationRule. Ele irá validar os dados quando é carregada pela primeira vez. Isso é bom se você está tentando recuperar os campos vazios ou nulos.

Você iria atualizar o seu elemento de ligação como esta:

<Binding 
    Source="{StaticResource CustomerKey}" 
    Path="LastName" 
    ValidatesOnExceptions="True" 
    ValidatesOnDataErrors="True" 
    UpdateSourceTrigger="LostFocus">
    <Binding.ValidationRules>
        <DataErrorValidationRule
            ValidatesOnTargetUpdated="True" />
    </Binding.ValidationRules>
</Binding>

Eu descobri a melhor maneira para eu lidar com isso era sobre o evento LostFocus da caixa de texto eu fazer algo assim

    private void dbaseNameTextBox_LostFocus(object sender, RoutedEventArgs e)
    {
        if (string.IsNullOrWhiteSpace(dbaseNameTextBox.Text))
        {
            dbaseNameTextBox.Text = string.Empty;
        }
    }

Em seguida, ele vê um erro

Eu já passei pelo mesmo problema e encontrou uma maneira ultra simples de resolver isso: no evento Loaded da sua janela, basta colocar txtLastName.Text = String.Empty. É isso aí!! Desde a propriedade de seu objeto mudou (sido definida como uma cadeia vazia), disparo da validação!

O código a seguir faz um loop sobre todos os controles e valida-los. Não necessariamente a maneira preferida, mas parece funcionar. Ele só faz TextBlocks e TextBoxes mas você pode facilmente mudá-lo.

public static class PreValidation
{

    public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
    {
        if (depObj != null)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                if (child != null && child is T)
                {
                    yield return (T)child;
                }

                foreach (T childOfChild in FindVisualChildren<T>(child))
                {
                    yield return childOfChild;
                }
            }
        }
    }


    public static void Validate(DependencyObject depObj)
    {
        foreach(var c in FindVisualChildren<FrameworkElement>(depObj))
        {
            DependencyProperty p = null;

            if (c is TextBlock)
                p = TextBlock.TextProperty;
            else if (c is TextBox)
                p = TextBox.TextProperty;

            if (p != null && c.GetBindingExpression(p) != null) c.GetBindingExpression(p).UpdateSource();
        }

    }
}

Basta ligar Validar em sua janela ou o controle e deve pré-validá-los para você.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top