【问题标题】:Trigger validation on click of Search button单击搜索按钮触发验证
【发布时间】:2016-03-28 05:53:04
【问题描述】:

我正在使用 Visual Studio 2015 Update 1 构建一个 MVVM Light WPF 应用程序。我有以下两个搜索字段:cmbSearchColumntxtSearchValue。当用户单击搜索按钮时,两者都不能为空。请注意,我为这两个字段设置了ValidationRules

这是相关的 XAML:

<TextBlock Grid.Row="1"
           Grid.Column="0"
           Style="{StaticResource FieldLabel}">
    Search Column
</TextBlock>
<StackPanel Grid.Row="1"
            Grid.Column="1"
            Style="{StaticResource ValidationStackPanel}">
    <ComboBox x:Name="cmbSearchColumn"
              DisplayMemberPath="MemberName"
              IsEditable="True"
              ItemsSource="{Binding SearchColumns}"
              SelectedValuePath="MemberValue"
              Style="{StaticResource ComboBoxStyle}">
        <ComboBox.SelectedItem>
            <Binding Mode="TwoWay"
                     Path="SelectedColumn}"
                     UpdateSourceTrigger="Explicit">
                <Binding.ValidationRules>
                    <helpers:NotEmptyStringValidationRule 
  Message="Search Column cannot be blank." ValidatesOnTargetUpdated="True" />
                </Binding.ValidationRules>
            </Binding>
        </ComboBox.SelectedItem>
    </ComboBox>
    <TextBlock Style="{StaticResource FieldLabelError}" 
  Text="{Binding (Validation.Errors)[0].ErrorContent, ElementName=cmbSearchColumn}" />
</StackPanel>
<TextBlock Grid.Row="2"
           Grid.Column="0"
           Padding="0 0 9 9"
           Style="{StaticResource FieldLabel}">
    Search Value
</TextBlock>
<StackPanel Grid.Row="1"
            Grid.Column="1"
            Style="{StaticResource ValidationStackPanel}">
    <TextBox x:Name="txtSearchValue" Style="{StaticResource FieldTextBox}">
        <TextBox.Text>
            <Binding Mode="TwoWay"
                     Path="SearchValue"
                     UpdateSourceTrigger="Explicit">
                <Binding.ValidationRules>
                    <helpers:NotEmptyStringValidationRule 
  Message="Search Value cannot be blank." ValidatesOnTargetUpdated="True" />
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>
    <TextBlock Style="{StaticResource FieldLabelError}" 
  Text="{Binding (Validation.Errors)[0].ErrorContent, ElementName=txtSearchValue}" />
</StackPanel>   
<Button Grid.Row="4"
    Grid.Column="1"
    Command="{Binding SearchEmployeesRelayCommand}"
    Content="Search"
    Style="{StaticResource FieldButton}" />

当应用加载时,它会立即在字段旁边显示错误,指出它们不能为空。但是,只有当用户单击“搜索”按钮时,我才需要触发对它们的验证。

我该怎么做?谢谢。

【问题讨论】:

  • 我还没有阅读您发布的全部代码,但我认为您应该使用 ICommand.CanExecute() 函数简单地禁用搜索按钮(当您的验证条件失败时返回 false) - 您还应该订阅更改您正在使用的属性的视图模型并相应地提高ICommand.CanExecuteChanged
  • 谢谢,@Maverik。去查找那些语法,看看如何正确地做到这一点:)
  • @Maverik 我认为 OP 要求相反。如果验证失败,他不想禁用该按钮。他希望验证错误消息仅在单击按钮时出现。
  • @qqww2 从表面上看,观察结果是正确的,但在我们的WPF chat room 中,Alex 来并发布了问题,其中包含更多上下文。这只是解决他的功能问题。这里没有提到 UX 解决方案。一旦我们在房间里讨论的事情达成一致,也许我会发布一个完整的解决方案
  • 如果您觉得可以,将ValidatesOnTargetUpdated 设置为False 会阻止程序加载时的第一次验证。

标签: c# wpf validation mvvm


【解决方案1】:

您可以使用INotifyDataErrorInfo

请注意,INotifyDataErrorInfo适用于添加到绑定的自定义规则。此答案中不包含自定义规则和 RelayCommand 的代码。

示例实现:

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;

public class PropertyErrors : INotifyDataErrorInfo
{
    private static readonly IReadOnlyList<object> EmptyErrors = new object[0];
    private readonly Action<DataErrorsChangedEventArgs> ownerOnErrorsChanged;
    private readonly Type type;
    private readonly ConcurrentDictionary<string, List<object>> propertyErrors = new ConcurrentDictionary<string, List<object>>();

    public PropertyErrors(INotifyDataErrorInfo owner, Action<DataErrorsChangedEventArgs> ownerOnErrorsChanged)
    {
        this.ownerOnErrorsChanged = ownerOnErrorsChanged;
        this.type = owner.GetType();
    }

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    public bool HasErrors => this.propertyErrors.Count > 0;

    public IEnumerable GetErrors(string propertyName)
    {
        Debug.Assert(this.type.GetProperty(propertyName) != null, $"The type {this.type.Name} does not have a property named {propertyName}");
        List<object> errors;
        return this.propertyErrors.TryGetValue(propertyName, out errors)
            ? errors
            : EmptyErrors;
    }

    public void Add(string propertyName, object error)
    {
        Debug.Assert(this.type.GetProperty(propertyName) != null, $"The type {this.type.Name} does not have a property named {propertyName}");
        this.propertyErrors.AddOrUpdate(
            propertyName,
            _ => new List<object> { error },
            (_, errors) => UpdateErrors(error, errors));

        this.OnErrorsChanged(new DataErrorsChangedEventArgs(propertyName));
    }

    public void Remove(string propertyName, Predicate<object> filter)
    {
        Debug.Assert(this.type.GetProperty(propertyName) != null, $"The type {this.type.Name} does not have a property named {propertyName}");
        List<object> errors;
        if (this.propertyErrors.TryGetValue(propertyName, out errors))
        {
            errors.RemoveAll(filter);
            if (errors.Count == 0)
            {
                this.Clear(propertyName);
            }
        }
    }

    public void Clear(string propertyName)
    {
        Debug.Assert(this.type.GetProperty(propertyName) != null, $"The type {this.type.Name} does not have a property named {propertyName}");
        List<object> temp;
        if (this.propertyErrors.TryRemove(propertyName, out temp))
        {
            this.OnErrorsChanged(new DataErrorsChangedEventArgs(propertyName));
        }
    }

    protected virtual void OnErrorsChanged(DataErrorsChangedEventArgs e)
    {
        this.ErrorsChanged?.Invoke(this, e);
        this.ownerOnErrorsChanged(e);
    }

    private static List<object> UpdateErrors(object error, List<object> errors)
    {
        if (!errors.Contains(error))
        {
            errors.Add(error);
        }

        return errors;
    }
}
using System;
using System.Collections;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;

public class ViewModel : INotifyPropertyChanged, INotifyDataErrorInfo
{
    private readonly PropertyErrors errors;
    private string searchText;

    public ViewModel()
    {
        this.SearchCommand = new RelayCommand(this.Search);
        this.errors = new PropertyErrors(this, this.OnErrorsChanged);
    }

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    public event PropertyChangedEventHandler PropertyChanged;

    public string SearchText
    {
        get { return this.searchText; }
        set
        {
            if (value == this.searchText)
            {
                return;
            }

            this.searchText = value;
            this.errors.Clear(nameof(this.SearchText));
            this.OnPropertyChanged();
        }
    }

    public bool HasErrors => this.errors.HasErrors;

    public ICommand SearchCommand { get; }

    public IEnumerable GetErrors(string propertyName) => this.errors.GetErrors(propertyName);

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private void Search()
    {
        if (string.IsNullOrEmpty(this.searchText))
        {
            this.errors.Add(nameof(this.SearchText), "Search text cannot be empty");
            return;
        }

        MessageBox.Show("searching");
    }

    protected virtual void OnErrorsChanged(DataErrorsChangedEventArgs e)
    {
        this.ErrorsChanged?.Invoke(this, e);
    }
}
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <TextBlock Grid.Row="0"
               Grid.Column="0"
               Text="Search text" />
    <TextBox x:Name="SearchTextBox"
             Grid.Row="0"
             Grid.Column="1">
        <TextBox.Text>
            <Binding Path="SearchText"
                     UpdateSourceTrigger="PropertyChanged">
                <Binding.ValidationRules>
                    <local:MustStartWithValidationRule StartsWith="a" />
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>
    <ItemsControl Grid.Row="0"
                  Grid.Column="2"
                  Margin="6,0,0,0"
                  ItemsSource="{Binding Path=(Validation.Errors),
                                        ElementName=SearchTextBox}">
        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="{x:Type ValidationError}">
                <TextBlock Foreground="Red"
                           Text="{Binding ErrorContent}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

    <Button Grid.Row="1"
            Grid.Column="1"
            Command="{Binding SearchCommand}"
            Content="Search" />
</Grid>

【讨论】:

    【解决方案2】:
    First name:<br>
      <input type="text" name="firstname" id="Fname" class="required" maxlength="50">
      <br>
      Last name:<br>
      <input type="text" name="lastname" id="lname" class="required" maxlength="50">
      <br>
      Email:<br>
      <input type="email" name="emailid" id="email" class="required" maxlength="50">
      <br>
        Phone No:-<br>
      <input type="text" maxlength="10" id="phoneno" class="required" pattern="[0-9]{10}"  >
      <br>
      Address:<br>
    
    <textarea rows="5" cols="25" id="address" class="required" maxlength="500">
    
    </textarea>
      <br><br >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
     <input type="button" value="submit"`onclick="validate()" />
    

    【讨论】:

      猜你喜欢
      • 2012-09-12
      • 2017-06-06
      • 1970-01-01
      • 2016-09-03
      • 1970-01-01
      • 2016-06-03
      • 1970-01-01
      • 1970-01-01
      • 2016-12-10
      相关资源
      最近更新 更多