WPF文本框失去焦点后第一次验证未触发

17

我正在尝试对一个WPF表单进行对象验证。当我在文本框中输入内容、失去焦点、回到文本框并擦除我写的所有内容时,验证会触发。但如果我只是加载WPF应用程序并且在文本框上按Tab键而不写任何内容或从文本框中擦除任何内容,则不会触发验证。

这是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;
            }
        }
    }

这里是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>
6个回答

20

如果你不介意在代码后台加入一些逻辑,你可以使用以下代码处理实际的LostFocus事件:

.xaml

<TextBox LostFocus="TextBox_LostFocus" ....

.xaml.cs

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

1
不幸的是,这不能应用于MVVM逻辑。当您不想修改代码时,有什么提示可以做到这一点吗? - FanaticD
1
我曾认为在MVVM中,视图中的代码“仅限于视图”,与视图模型无关是可以的。 - Daniel Möller

13

很不幸,这是设计上的限制。WPF 验证仅在控件中的值更改时才触发。

虽然难以置信,但事实如此。到目前为止,WPF验证是一个巨大的惯例性问题 - 它非常糟糕。

但是,你可以从控件属性获取绑定表达式并手动调用验证。虽然很烦人,但它确实可行。


6

请看ValidationRule的ValidatesOnTargetUpdated属性。它会在数据首次加载时进行验证。如果您想捕捉空或null字段,这是很好的选择。

您可以像这样更新绑定元素:

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

1
如果您使用反射器进行检查,您会发现“ValidatesOnTargetUpdated”属性已经被DataErrorValidationRule设置为true。它调用了一个带有参数的继承构造函数,该参数指定了这一点。因此,按照所示添加属性赋值不会有任何区别。虽然这是个好想法 - 我一直在尝试解决类似的问题,这看起来确实很有前途。 - Phil
这个对我在自定义规则上起作用了!非常感谢你,伙计! - JFTxJ

1

我发现我处理这个问题的最佳方法是在文本框的失去焦点事件上执行类似以下的操作

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

然后它遇到了一个错误


这是一个答案还是你正在寻求帮助解决问题? - animuson

0
我曾经遇到过同样的问题,并找到了一种超级简单的解决方法:在窗口的Loaded事件中,只需将txtLastName.Text = String.Empty放置即可。就是这样!由于您对象的属性已更改(被设置为空字符串),因此验证会触发!

3
谢谢,但那个解决方案不可扩展,而且在所有页面上维护起来会很困难! - azamsharp

0
以下代码循环遍历所有控件并验证它们。这不一定是首选的方法,但似乎可以工作。它只处理 TextBlock 和 TextBox,但你可以轻松更改它。
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();
        }

    }
}

只需在您的窗口或控件上调用Validate方法,它就会为您进行预验证。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接