gpt4 book ai didi

c# - 如何使用 DataAnnotations 检查 WPF 中的无效数字

转载 作者:行者123 更新时间:2023-11-30 23:16:37 26 4
gpt4 key购买 nike

我正在开发一个采用 MVVM 模式的 WPF 应用程序,我在其中使用 DataAnnotations 进行验证。所以我在这个 article 中实现了类似的解决方案.

然后我尝试向我的 View 模型添加一个属性 - 称为“年龄” - 它只接受数字并且范围在 1 到 100 之间。

    [RegularExpression(@"^[0-9]{1,3}$", ErrorMessage = "Please enter a valid number")]
[Range(1, 100, ErrorMessage = "Age should be between 1 to 100")]
/// <summary>
/// Gets or sets the age.
/// </summary>
public int Age
{
get { return GetValue(() => Age); }
set { SetValue(() => Age, value); }
}

在我的 WPF 窗口上,我得到了一个绑定(bind)到 Age 的文本框:

    <TextBox x:Name="tbx_age"
ToolTip="The age"
Text="{Binding Age, NotifyOnSourceUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
</TextBox>

当我启动应用程序时,文本框预分配了零(“0”)。当我用“1a”替换文本框中的零时,我收到错误消息“无法转换值‘1a’”。这不是来 self 的代码的常规消息,我无法解释它的来源。我是否在正则表达式或其他方面犯了错误?

我已经将我的测试项目上传到 GitHub: Repository我指的项目名为“Validation_DataAnnotations”。

提前致谢!

这是我用于通知和验证的 PropertyChangedNotification 类:

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

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

#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;
NotifyPropertyChanged(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)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}

string error = string.Empty;
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;
}

return error;
}

#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 NotifyPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);

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

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

#endregion // INotifyPropertyChanged 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
}

最佳答案

因为 int 属性不能永远设置为 int 值以外的任何值,所以如果您设置,您的属性 setter 将永远不会被调用将 TextBox 的 Text 属性设置为“1a”。世界上没有正则表达式或数据注释可以解决这个问题。

要自定义当 WPF 运行时在设置属性之前将值“1a”转换为 int 时出现的错误消息,您可以使用 ValidationRule:

<TextBox>
<TextBox.Text>
<Binding Path="Age" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:StringToIntValidationRule ValidationStep="RawProposedValue"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
...
</TextBox>

public class StringToIntValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
int i;
if (int.TryParse(value.ToString(), out i))
return new ValidationResult(true, null);

return new ValidationResult(false, "Please enter a valid integer value.");
}
}

以下博客文章提供了完整示例和更多相关信息:https://blog.magnusmontin.net/2013/08/26/data-validation-in-wpf/

请注意,验证其 Age 属性是否设置为 int 值不是 View 模型的责任。这是控件或 View 的责任。

关于c# - 如何使用 DataAnnotations 检查 WPF 中的无效数字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41936211/

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