wpf - WPF搜索表单中的多字段验证器

标签 wpf validation mvvm requiredfieldvalidator

我有一个相当简单的 View ,可以用作搜索表单。有两个组合框和一个带有“搜索”按钮的文本框。如果下拉列表中没有选择项或文本框为空,则无法执行搜索。我在应用程序中的几个地方都使用过IDataErrorInfo,但它似乎不适合这里(我没有“SearchPageModel”,而且我不确定如何在viewmodel上实现它),以及由于完全缺乏验证器控件,我不确定该怎么做。我只想显示一条有关填写所有信息的消息,如果用户尝试进行搜索而没有事先这样做的话。最简单的方法是什么?

更新:
按照第一个答案的链接中的建议,我创建了一个验证规则并修改了文本框,使其看起来像这样:

    <TextBox Grid.Column="1" Grid.Row="3" Name="tbxPartNumber" Margin="6">
        <TextBox.Text>
            <Binding Path="SelectedPartNumber" UpdateSourceTrigger="PropertyChanged">
                <Binding.ValidationRules>
                    <local:RequiredTextValidation/>
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>

但是,这是一个新问题:当我转到屏幕上并在文本框中输入内容,然后将其删除时,该框将按预期突出显示为红色。 Validation.GetHasErrors(tbxPartNumber)返回true。如果我转到屏幕并且根本不与文本框互动,则Validation.GetHasErrors(tbxPartNumber)返回false。看来它仅在我修改文本时才起作用...如果用户只是出现并单击搜索而不输入任何内容,它将无法验证。有没有解决的办法?

最佳答案

article on MSDN提供了一个很好的示例,说明如何验证类似内容,检查各个子节(“验证用户提供的数据”)。关键是使用 ValidationRules 并检查对话框的整个逻辑树中是否有错误。

编辑:在ValidationRule上设置 ValidatesOnTargetUpdated ="True"应该可以解决问题。实际上,MSDN文档中有关该属性的示例正是这种情况。在这种情况下,可能还需要添加this answer

Edit2:这是我拥有的导致验证失败的完整代码:

<Window x:Class="Test.Dialogs.SearchDialog"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:diag="clr-namespace:Test.Dialogs"
        xmlns:m="clr-namespace:HB.Xaml"
        Title="Search" SizeToContent="WidthAndHeight" ResizeMode="NoResize"
        Name="Window" DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
        <Grid.Resources>
            <Style x:Key="BaseStyle" TargetType="{x:Type FrameworkElement}">
                <Setter Property="Margin" Value="3"/>
                <Setter Property="VerticalAlignment" Value="Center"/>
            </Style>
            <Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource BaseStyle}"/>
            <Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource BaseStyle}"/>
            <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource BaseStyle}"/>
            <Style TargetType="{x:Type Button}" BasedOn="{StaticResource BaseStyle}"/>
        </Grid.Resources>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.Children>
            <TextBlock Grid.Column="0" Grid.Row="0" Text="Scope:"/>
            <ComboBox Grid.Column="1" Grid.Row="0" ItemsSource="{m:EnumItems {x:Type diag:SearchDialog+ScopeMode}}">
                <ComboBox.SelectedItem>
                    <Binding Path="Scope">
                        <Binding.ValidationRules>
                            <diag:HasSelectionValidationRule />
                        </Binding.ValidationRules>
                    </Binding>
                </ComboBox.SelectedItem>
            </ComboBox>

            <TextBlock Grid.Column="0" Grid.Row="1" Text="Direction:"/>
            <ComboBox Grid.Column="1" Grid.Row="1" ItemsSource="{m:EnumItems {x:Type diag:SearchDialog+DirectionMode}}">
                <ComboBox.SelectedItem>
                    <Binding Path="Direction">
                        <Binding.ValidationRules>
                            <diag:HasSelectionValidationRule />
                        </Binding.ValidationRules>
                    </Binding>
                </ComboBox.SelectedItem>
            </ComboBox>


            <TextBlock Grid.Column="0" Grid.Row="2" Text="Expression:"/>
            <TextBox Name="tb" Grid.Column="1" Grid.Row="2">
                <TextBox.Text>
                    <Binding Path="Expression" UpdateSourceTrigger="PropertyChanged">
                        <Binding.ValidationRules>
                            <diag:StringNotEmptyValidationRule />
                        </Binding.ValidationRules>
                    </Binding>
                </TextBox.Text>
            </TextBox>

            <Button Grid.Column="1" Grid.Row="3" Content="Search" Click="Search_Click"/> 
        </Grid.Children>
    </Grid>
</Window>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace Test.Dialogs
{
    /// <summary>
    /// Interaction logic for SearchDialog.xaml
    /// </summary>
    public partial class SearchDialog : Window
    {
        public enum ScopeMode { Selection, Document, Solution }
        public enum DirectionMode { Up, Down }

        public ScopeMode Scope { get; set; }
        public DirectionMode Direction { get; set; }
        private string _expression = String.Empty;
        public string Expression
        {
            get { return _expression; }
            set { _expression = value; }
        }


        public SearchDialog()
        {
            InitializeComponent();
        }

        private void Search_Click(object sender, RoutedEventArgs e)
        {
            (sender as Button).Focus();
            if (IsValid(this)) MessageBox.Show("<Searching>");
            else MessageBox.Show("Errors!");
        }

        bool IsValid(DependencyObject node)
        {
            if (node != null)
            {
                bool isValid = Validation.GetHasError(node);
                if (!isValid)
                {
                    if (node is IInputElement) Keyboard.Focus((IInputElement)node);
                    return false;
                }
            }
            foreach (object subnode in LogicalTreeHelper.GetChildren(node))
            {
                if (subnode is DependencyObject)
                {
                    if (IsValid((DependencyObject)subnode) == false) return false;
                }
            }
            return true;
        }
    }

    public class HasSelectionValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            if (value == null)
            {
                return new ValidationResult(false, "An item needs to be selected.");
            }
            else
            {
                return new ValidationResult(true, null);
            }
        }
    }

    public class StringNotEmptyValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            if (String.IsNullOrWhiteSpace(value as string))
            {
                return new ValidationResult(false, "The search expression is empty.");
            }
            else
            {
                return new ValidationResult(true, null);
            }
        }
    }
}

关于wpf - WPF搜索表单中的多字段验证器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6018167/

相关文章:

c# - 使 WPF 窗口免疫显示桌面(防止隐藏)

c#处理空格键事件

python - Django - 如何在多个表单上使用完全相同的 clean() 方法

c# - 可以显示来自资源 URI 属性的 .png 和基于矢量的图像的 MenuItem

c# - WPF - 当我使用 MVVM 从 MainWindow 调用 ChildWindow 时如何隐藏按钮

c#-4.0 - 有没有办法在不使用插件的情况下以Xamarin形式(软件模式-mvvm)创建弹出窗口?

wpf - 如何使 Viewbox 的子元素不可缩放

c# - 数据绑定(bind)到 WPF 中的文本框

java - 如何修复 'parentMessageSource of the messageSourceBean' 使其不总是为空

javascript - Javascript 中的表单验证