gpt4 book ai didi

c# - 首次加载窗口时隐藏 WPF 验证错误

转载 作者:行者123 更新时间:2023-12-04 18:05:10 33 4
gpt4 key购买 nike

当我第一次加载窗口时,按钮是可见的并且验证中没有错误(没有红线圆形文本框)。

what window looks like when first loaded

在文本框中键入值时,验证规则会正常工作。

如果可能的话,我希望在开始时隐藏按钮,并在我开始向框中输入文本时启动验证规则。

这是我到目前为止的代码。 xaml:

 <TextBox x:Name="txtName" HorizontalAlignment="Left" Height="23" Margin="156,119,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" Validation.ErrorTemplate="{StaticResource ValidationErrorTemplate}">
<TextBox.Text>
<Binding Path="Name" UpdateSourceTrigger="PropertyChanged" NotifyOnSourceUpdated="True">
<Binding.ValidationRules>
<local:ValidationTest/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>


<Button x:Name="btn1" Content="Button" HorizontalAlignment="Left" Margin="85,221,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=(Validation.HasError), ElementName=txtName}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="Visibility" Value="Visible"></Setter>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

我的验证逻辑:

class ValidationTest : ValidationRule
{
private int result;
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
if (value == null || string.IsNullOrEmpty(value.ToString()))
{
return new ValidationResult(false, "Value cannot be empty.");
}
if (value.ToString().Length > 4)
{
return new ValidationResult(false, "Name cannot be more than 20 characters long.");
}
return ValidationResult.ValidResult;
}
}

我正在使用的错误模板:

    <Window.Resources>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
<ControlTemplate x:Key="ValidationErrorTemplate">
<DockPanel>
<Border BorderBrush="Red" BorderThickness="1.5">
<AdornedElementPlaceholder x:Name="ErrorAdorner"></AdornedElementPlaceholder>
</Border>
</DockPanel>
</ControlTemplate>
</Window.Resources>

我尝试在窗口加载时使用 txtName.GetBindingExpression(TextBox.TextProperty).UpdateSource(); 更新绑定(bind),但这显示了验证错误(文本框周围的红线)。但是,该按钮是隐藏的,那么有什么方法可以隐藏验证错误,直到用户在文本框中键入文本?

what i get when i update the binding when the windows loads

我的处理方式是否正确?

最佳答案

我知道我参加派对有点晚了,但我在想做同样的事情时遇到了这个问题。关于使用标志来控制何时执行验证,我唯一不喜欢的是您需要在代码中的某个位置设置 DoValidation 标志,我希望它有点更多“自动化”。

网上找了好几个例子,好像都是用 bool 标志的方法。我找到了 this MSDN Article并以此为基础,然后调整代码。

我想出了一个似乎非常有效的解决方案。简而言之,我所做的基本上是创建另一个 Dictionary 来跟踪:

  1. 何时应执行验证。
  2. 存储验证的状态(有效、无效)。

我只想在第一次更新后执行验证,所以首要任务是决定是否应该执行验证。 Validation 的第一次运行将参数存储在 Dictionary 中,然后下一次如果参数存在,它会执行验证并存储 true/false(无效/有效)结果。这也是告知模型是否已验证以及是否有效的便捷方式,因此我还添加了一个参数/标志以简单地返回是否有任何结果和验证状态。这对于绑定(bind)命令启用/禁用特别有用。

我是这样实现的:

基本属性验证模型:

public abstract class PropertyValidation : INotifyPropertyChanged, IDataErrorInfo
{
#region Fields

private readonly Dictionary<string, object> _values = new Dictionary<string, object>();

/// <summary>
/// This holds the list of validation results and controls when the validation should be
/// performed and if the validation is valid.
/// </summary>
private Dictionary<string, bool> _validationResults { get; set; } = new Dictionary<string, bool>();

#endregion

#region Protected

/// <summary>
/// Sets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertySelector">Expression tree contains the property definition.</param>
/// <param name="value">The property value.</param>
protected void SetValue<T>(Expression<Func<T>> propertySelector, T value)
{
string propertyName = GetPropertyName(propertySelector);

SetValue<T>(propertyName, value);
}

/// <summary>
/// Sets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertyName">The name of the property.</param>
/// <param name="value">The property value.</param>
protected void SetValue<T>(string propertyName, T value)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}

_values[propertyName] = value;
OnPropertyChanged(propertyName);
}

/// <summary>
/// Gets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertySelector">Expression tree contains the property definition.</param>
/// <returns>The value of the property or default value if not exist.</returns>
protected T GetValue<T>(Expression<Func<T>> propertySelector)
{
string propertyName = GetPropertyName(propertySelector);

return GetValue<T>(propertyName);
}

/// <summary>
/// Gets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertyName">The name of the property.</param>
/// <returns>The value of the property or default value if not exist.</returns>
protected T GetValue<T>(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}

object value;
if (!_values.TryGetValue(propertyName, out value))
{
value = default(T);
_values.Add(propertyName, value);
}

return (T)value;
}

/// <summary>
/// Validates current instance properties using Data Annotations.
/// </summary>
/// <param name="propertyName">This instance property to validate.</param>
/// <returns>Relevant error string on validation failure or <see cref="System.String.Empty"/> on validation success.</returns>
protected virtual string OnValidate(string propertyName)
{
string error = string.Empty;

if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}

//Check if the Field has been added, this keeps track of when the validation
//is performed.
if (_validationResults.Any(x => x.Key == propertyName))
{
var value = GetValue(propertyName);
var results = new List<System.ComponentModel.DataAnnotations.ValidationResult>(1);
var result = Validator.TryValidateProperty(
value,
new ValidationContext(this, null, null)
{
MemberName = propertyName
},
results);

if (!result)
{
var validationResult = results.First();
error = validationResult.ErrorMessage;

//Store a true result in the validation to set the error.
_validationResults[propertyName] = true;
}
else
{
//If the Validation has been run and not invalid make sure the
//paramter in the list is cleared, otherwise validation would
//always return invalid once it is invalidated.
_validationResults[propertyName] = false;
}
}
else
{
//This is the first run of the Validation, simply store the paramter
//in the validation list and wait until next time to validate.
_validationResults.Add(propertyName, true);
}

//Notify that things have changed
OnPropertyChanged("IsValid");

//Return the actual result
return error;
}

#endregion

#region Public

/// <summary>
/// This returns if the Validation is Valid or not
/// </summary>
/// <returns>True if the Validation has been perfomed and if there are not
/// true values. Will return false until the validation has been done once.</returns>
public bool IsValid {
get { return (!_validationResults.Any(x => x.Value) && (_validationResults.Count > 0)); }
}

/// <summary>
/// Clears/Reset the Validation
/// </summary>
public void ClearValidation()
{
_validationResults.Clear();
}

#endregion

#region Change Notification

/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;

/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.VerifyPropertyName(propertyName);

PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}

protected void OnPropertyChanged<T>(Expression<Func<T>> propertySelector)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
string propertyName = GetPropertyName(propertySelector);
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

#endregion // IOnPropertyChanged Members

#region Data Validation

string IDataErrorInfo.Error {
get {
throw new NotSupportedException("IDataErrorInfo.Error is not supported, use IDataErrorInfo.this[propertyName] instead.");
}
}

string IDataErrorInfo.this[string propertyName] {
get {
return OnValidate(propertyName);
}
}

#endregion

#region Privates

private string GetPropertyName(LambdaExpression expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
{
throw new InvalidOperationException();
}

return memberExpression.Member.Name;
}

private object GetValue(string propertyName)
{
object value;
if (!_values.TryGetValue(propertyName, out value))
{
var propertyDescriptor = TypeDescriptor.GetProperties(GetType()).Find(propertyName, false);
if (propertyDescriptor == null)
{
throw new ArgumentException("Invalid property name", propertyName);
}

value = propertyDescriptor.GetValue(this);
_values.Add(propertyName, value);
}

return value;
}

#endregion

#region Debugging

/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;

if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}

/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }

#endregion // Debugging Aides
}

型号:

public class MyModel : PropertyValidation
{
[Required(ErrorMessage = "Name must be specified")]
[MaxLength(50, ErrorMessage = "Name too long, Name cannot contain more than 50 characters")]
public string Name {
get { return GetValue(() => Name); }
set { SetValue(() => Name, value); }
}

[Required(ErrorMessage = "Description must be specified")]
[MaxLength(150, ErrorMessage = "Description too long, Description cannot contain more than 150 characters")]
public string Description {
get { return GetValue(() => Description); }
set { SetValue(() => Description, value); }
}
}

错误模板:

<ControlTemplate x:Key="ValidationErrorTemplate">
<DockPanel LastChildFill="true">
<Border Background="Red" DockPanel.Dock="right"
Margin="-20,0,0,0" Width="10" Height="10" CornerRadius="10"
ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
<TextBlock Text="!" VerticalAlignment="center" HorizontalAlignment="center" FontWeight="Bold" Foreground="white"/>
</Border>
<AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >
<Border BorderBrush="red" BorderThickness="1" >
<Border.Effect>
<BlurEffect Radius="5" />
</Border.Effect>
</Border>
</AdornedElementPlaceholder>

</DockPanel>
</ControlTemplate>

数据模板/表格:

<DataTemplate x:Key="MyModelDetailsTemplate" DataType="{x:Type data:MyModel}" >

<StackPanel Grid.IsSharedSizeScope="True">

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" SharedSizeGroup="Labels" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<Label Grid.Column="0">Name</Label>
<TextBox x:Name="Name"
Grid.Column="1"
MinWidth="150"
Validation.ErrorTemplate="{StaticResource ValidationErrorTemplate}"
Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" SharedSizeGroup="Labels" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<Label Grid.Column="0">Description</Label>
<TextBox Grid.Column="1"
Validation.ErrorTemplate="{StaticResource ValidationErrorTemplate}"
Text="{Binding Description, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" AcceptsReturn="True" VerticalAlignment="Stretch" />

</Grid>
</StackPanel>

</DataTemplate>

RelayCommand(完整性)

public class RelayCommand : ICommand
{
private Action<object> execute;

private Predicate<object> canExecute;

private event EventHandler CanExecuteChangedInternal;

public RelayCommand(Action<object> execute) : this(execute, DefaultCanExecute)
{
}

public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}

if (canExecute == null)
{
throw new ArgumentNullException("canExecute");
}

this.execute = execute;
this.canExecute = canExecute;
}

public event EventHandler CanExecuteChanged {
add {
CommandManager.RequerySuggested += value;
this.CanExecuteChangedInternal += value;
}

remove {
CommandManager.RequerySuggested -= value;
this.CanExecuteChangedInternal -= value;
}
}

public bool CanExecute(object parameter)
{
return this.canExecute != null && this.canExecute(parameter);
}

public void Execute(object parameter)
{
this.execute(parameter);
}

public void OnCanExecuteChanged()
{
EventHandler handler = this.CanExecuteChangedInternal;
if (handler != null)
{
//DispatcherHelper.BeginInvokeOnUIThread(() => handler.Invoke(this, EventArgs.Empty));
handler.Invoke(this, EventArgs.Empty);
}
}

public void Destroy()
{
this.canExecute = _ => false;
this.execute = _ => { return; };
}

private static bool DefaultCanExecute(object parameter)
{
return true;
}
}

View 模型:

** 请注意,这里不需要 PropertyValidation,可以使用单独的 INotifyPropertyChanged 基础,我只将它用于 OnPropertyChanged 通知并保持简单 **

public class PageHomeVM : PropertyValidation
{
private ICommand saveCommand;
public ICommand SaveCommand {
get {
return saveCommand;
}
set {
saveCommand = value;
OnPropertyChanged();
}
}

public MyModel MyModel { get; set; } = new MyModel();

public PageHomeVM()
{
SaveCommand = new RelayCommand(SaveRecord, p => MyModel.IsValid);
MyModel.ClearValidation();
}

public void SaveRecord(object p)
{
//Perform the save....
}
}

查看:

<pages:BasePage.DataContext>
<ViewModels:PageHomeVM/>
</pages:BasePage.DataContext>

<StackPanel>
<Label Content="MyModel Details"/>

<ContentPresenter ContentTemplate="{StaticResource MyModelDetailsTemplate}" Content="{Binding MyModel}" />

<Button x:Name="btnSave"
Command="{Binding SaveCommand}"
Width="75"
HorizontalAlignment="Right">Save</Button>

</StackPanel>

我希望这有助于...

关于c# - 首次加载窗口时隐藏 WPF 验证错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28463443/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com