我们都知道糟糕的WPF验证是开箱即用的。我正在尝试一个非常简单的事情,由于某种原因,它总是失败。我有一个TextBox,我唯一的要求是验证用户在TextBox中输入的内容。 TextBox绑定到具有FirstName和LastName属性的Customer对象。
以下是XAML代码:
<TextBox Style="{StaticResource TextBoxStyle}" Grid.Column="1" Grid.Row="0" Height="20" Width="100" Margin="10">
<TextBox.Text>
<Binding Path="FirstName" >
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
这是Customer类的FirstName属性:
public string FirstName
{
get { return _firstName;}
set
{
if(String.IsNullOrEmpty(value))
throw new ApplicationException("FirstName cannot be null or empty!");
_firstName = value;
OnPropertyChanged("FirstName");
}
}
即使我在FirstName(value)为null或为空时抛出异常,只有在TextBox中键入内容然后将其删除然后将其关闭时才会处理。原因是它依赖于属性改变事件。但即使我将这个TextBox绑定放在Focus上,它也不会触发验证。
更新:
处理此问题的最丑陋方法之一是将String.Empty分配给Window.Loaded事件上的TextBox:
void AddCustomerWindow_Loaded(object sender, RoutedEventArgs e)
{
// get all the textboxes and set the property to empty strings!
txtFirstName.Text = String.Empty;
txtLastName.Text = String.Empty;
}
以下是绑定代码:
public AddCustomerWindow()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(AddCustomerWindow_Loaded);
gvAddCustomer.DataContext = new Customer();
}
答案 0 :(得分:0)
在您的示例XAML代码中,我没有看到任何绑定。我假设TextBox的Text属性绑定到FirstName?
其次:首先初始化FirstName,还是在TextBox检索时返回null
?绑定到null
值总是会产生奇怪的行为......
尝试在财产中说明这一点:
public string FirstName
{
get
{
if (_firstName == null)
{
_firstName = String.Empty;
}
return _firstName;
}
set
{
if (String.IsNullOrEmpty(value))
throw new ApplicationException("FirstName cannot be null or empty!");
_firstName = value;
OnPropertyChanged("FirstName");
}
}
答案 1 :(得分:0)
我认为你只需要指定UpdateSourceTrigger就可以了(注意:我通常也会添加ValidatesOnDataErrors = true):
答案 2 :(得分:0)
您最初可以设置私有变量,而不必在开始时设置所有值,或者使用if语句:
private string _FirstName = String.Empty;
public string FirstName
{
get { return _FirstName; }
set
{
// Probably check for null and handle it here first
_FirstName = value;
OnPropertyChanged("FirstName");
}
}
答案 3 :(得分:0)
我使用的解决方案是连接控件,以便在失去焦点时更新自己的绑定源。这使控件在用户选中或点击一次后验证。
public AddCustomerWindow()
{
InitializeComponent();
this.Loaded += AddCustomerWindow_Loaded;
gvAddCustomer.DataContext = new Customer();
}
void AddCustomerWindow_Loaded(object sender, RoutedEventArgs e)
{
// Manually wire up textboxes of specific interest.
UpdateSourceOnLostFocus(new TextBox[] {txtFirstName, txtLastName},
TextBox.TextProperty);
// Or, wire up ALL controls of a given type with the help of Telerik's
// ChildrenOfType extension, using Linq to filter the list where appropriate.
UpdateSourceOnLostFocus(this.ChildrenOfType<RadDatePicker>()
.Where(picker => !picker.IsReadOnly),
RadDatePicker.SelectedValueProperty);
}
void UpdateSourceOnLostFocus<T>(IEnumerable<T> controls, DependencyProperty prop) where T : Control
{
controls.ToList()
.ForEach(ctrl =>
{
var binding = BindingOperations.GetBindingExpression(ctrl, prop);
if (binding != null)
ctrl.LostFocus += (sender, args) => binding.UpdateSource();
});
}
如果您还希望控件在加载时验证 - 即之前用户选项卡/点击它们 - 那么您可以编写类似于上面的UpdateSourceNow方法,并从AddCustomerWindow_Loaded调用它代替。
但是,我宁愿等到访问每个控件,因为当用户想要输入新的Model对象实例时,如果他们立即看到一个装满红色框的屏幕,对用户来说可能会有些警觉。
答案 4 :(得分:0)