我正在使用Visual Studio 2015 Update 1构建MVVM Light WPF应用程序。我有以下两个搜索字段:cmbSearchColumn
和txtSearchValue
。当用户单击“搜索”按钮时,两者都不能为空。请注意,我已为这两个字段设置了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}" />
当应用加载时,它会立即显示字段旁边的错误,表示它们不能为空。但是,我只需在用户单击“搜索”按钮时触发验证。
我该怎么做?感谢。
答案 0 :(得分:5)
您可以使用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>
答案 1 :(得分:-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 >
<input type="button" value="submit"`onclick="validate()" />