开发者

Multiple field validator in WPF search form

I've got a fairly simple view which functions as a search form. There are two comboboxes and a textbox, with a "search" button. The search cannot be performed if there is no selection in either dropdown or if the textbox is empty. I've used IDataErrorInfo in a few places in my application but it just doesn't seem to fit here (I don't have a 'SearchPageModel', and I'm not sure how I would implement it on the viewmodel), and with a complete lack of validator controls, I'm not sure how to go about this. I just want to show a message about filling in all the information if a user tries to search without previously doing so. What's the simplest way?

UPDATE: Following the advice in the link from the first answer, I created a validation rule and modified my textbox to look like this:

    <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>

However, here is a new issue: when I go to the screen and enter something into the textbox, then delete it, the box highlights in red, as expected. Validation.GetHasErrors(tbxPartNumber) returns true. If I go to the screen and do not interract with the textbox at all, Validation.GetHasErrors(tbxPartNumber) returns false. It appears that it only works when I modify the text... it won't validate if the user just shows up and clicks search without typing anything. I开发者_StackOverflow中文版s there a way around this?


This article on MSDN gives a good example of how to validate things like that, check the respective sub-section ("Validating User-Provided Data"). The key is using ValidationRules and checking the whole logical tree of the dialog for errors.

Edit: Setting ValidatesOnTargetUpdated="True" on the ValidationRule should do the trick here. In fact the example in the MSDN documentation of that property is exactly this scenario. Further this answer might be of interest in that context.

Edit2: Here's the full code i have which causes the validation to fail:

<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);
            }
        }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜