如果验证失败,请禁用 WPF 中的保存按钮
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/757590/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Disable Save button in WPF if validation fails
提问by Mitch
I've adopted what appears to be the standard way of validating textboxes in WPF using the IDataErrorInfo interface and styles as shown below. However, how can I disable the Save button when the page becomes invalid? Is this done somehow through triggers?
我采用了似乎是使用 IDataErrorInfo 接口和样式验证 WPF 中文本框的标准方法,如下所示。但是,如何在页面无效时禁用“保存”按钮?这是通过触发器以某种方式完成的吗?
Default Public ReadOnly Property Item(ByVal propertyName As String) As String Implements IDataErrorInfo.Item
Get
Dim valid As Boolean = True
If propertyName = "IncidentCategory" Then
valid = True
If Len(IncidentCategory) = 0 Then
valid = False
End If
If Not valid Then
Return "Incident category is required"
End If
End If
Return Nothing
End Get
End Property
<Style TargetType="{x:Type TextBox}">
<Setter Property="Margin" Value="3" />
<Setter Property="Height" Value="23" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder Name="MyAdorner" />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
回答by Josh G
A couple of things:
几件事:
First, I would recommend using the RoutedCommand ApplicationCommands.Save
for implementing the handling of the save button.
首先,我建议使用 RoutedCommandApplicationCommands.Save
来实现保存按钮的处理。
If you haven't checked out the WPF Command model, you can get the scoop here.
如果您尚未查看 WPF 命令模型,您可以在此处获取独家新闻。
<Button Content="Save" Command="Save">
Now, to implement the functionality, you can add a command binding to the Window/UserControl or to the Button itself:
现在,要实现该功能,您可以将命令绑定添加到 Window/UserControl 或 Button 本身:
<Button.CommandBindings>
<CommandBinding Command="Save"
Executed="Save_Executed" CanExecute="Save_CanExecute"/>
</Button.CommandBindings>
</Button>
Implement these in code behind:
在后面的代码中实现这些:
private void Save_Executed(object sender, ExecutedRoutedEventArgs e)
{
}
private void Save_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
In Save_CanExecute
, set e.CanExecute
based on the validity of the binding on the text box.
中Save_CanExecute
,e.CanExecute
根据文本框上绑定的有效性进行设置。
If you want to implement using the MVVM (Model-View-ViewModel) design pattern, check out Josh Smith's post on CommandSinkBinding.
如果您想使用 MVVM (Model-View-ViewModel) 设计模式来实现,请查看 Josh Smith 在CommandSinkBinding上的帖子。
One final note: If you want the enable/disable to be updated as soon as the value in the TextBox
is changed, set UpdateSourceTrigger="PropertyChanged"
on the binding for the TextBox
.
最后一个注意事项:如果您希望在 中的值TextBox
更改后立即更新启用/禁用UpdateSourceTrigger="PropertyChanged"
,请在TextBox
.
EDIT: If you want to validate/invalidate based on all of the bindings in the control, here are a few suggestions.
编辑:如果您想根据控件中的所有绑定来验证/无效,这里有一些建议。
1) You are already implementing IDataErrorInfo
. Try implementing the IDataErrorInfo.Error
property such that it returns the string that is invalid for all of the properties that you are binding to. This will only work if your whole control is binding to a single data object. Set e.CanExecute = string.IsNullOrEmpty(data.Error);
1) 您已经在实施IDataErrorInfo
. 尝试实现该IDataErrorInfo.Error
属性,使其返回对您绑定到的所有属性无效的字符串。这仅在您的整个控件绑定到单个数据对象时才有效。放e.CanExecute = string.IsNullOrEmpty(data.Error);
2) Use reflection to get all of the public static DependencyProperties on the relevant controls. Then call BindingOperations.GetBindingExpression(relevantControl, DependencyProperty)
in a loop on each property so you can test the validation.
2)使用反射获取相关控件上的所有公共静态DependencyProperties。然后BindingOperations.GetBindingExpression(relevantControl, DependencyProperty)
在每个属性上调用循环,以便您可以测试验证。
3) In the constructor, manually create a collection of all bound properties on nested controls. In CanExecute, iterate through this collection and validate each DependencyObject
/DepencyProperty
combination by using BindingOperation.GetBindingExpression()
to get expressions and then examining BindingExpression.HasError
.
3) 在构造函数中,手动创建嵌套控件上所有绑定属性的集合。在 CanExecute 中,遍历此集合并通过使用获取表达式然后检查 来验证每个DependencyObject
/DepencyProperty
组合。BindingOperation.GetBindingExpression()
BindingExpression.HasError
回答by ghord
I've created attached property just for this:
我为此创建了附加属性:
public static class DataErrorInfoHelper
{
public static object GetDataErrorInfo(ButtonBase obj)
{
return (object)obj.GetValue(DataErrorInfoProperty);
}
public static void SetDataErrorInfo(ButtonBase obj, object value)
{
obj.SetValue(DataErrorInfoProperty, value);
}
// Using a DependencyProperty as the backing store for DataErrorInfo. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataErrorInfoProperty =
DependencyProperty.RegisterAttached("DataErrorInfo", typeof(object), typeof(DataErrorInfoHelper), new PropertyMetadata(null, OnDataErrorInfoChanged));
private static void OnDataErrorInfoChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var button = d as ButtonBase;
if (button.Tag == null)
button.Tag = new DataErrorInfoContext { Button = button };
var context = button.Tag as DataErrorInfoContext;
if(e.OldValue != null)
{
PropertyChangedEventManager.RemoveHandler(((INotifyPropertyChanged)e.OldValue), context.Handler, string.Empty);
}
var inotify = e.NewValue as INotifyPropertyChanged;
if (inotify != null)
{
PropertyChangedEventManager.AddHandler(inotify, context.Handler, string.Empty);
context.Handler(inotify, new PropertyChangedEventArgs(string.Empty));
}
}
private class DataErrorInfoContext
{
public ButtonBase Button { get; set; }
public void Handler(object sender, PropertyChangedEventArgs e)
{
var dei = sender as IDataErrorInfo;
foreach (var property in dei.GetType().GetProperties())
{
if (!string.IsNullOrEmpty(dei[property.Name]))
{
Button.IsEnabled = false;
return;
}
}
Button.IsEnabled = string.IsNullOrEmpty(dei.Error);
}
}
}
I'm using it like this on my forms:
我在我的表单上这样使用它:
<TextBlock Margin="2">e-mail:</TextBlock>
<TextBox Margin="2" Text="{Binding Email, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
<!-- other databindings--->
<Button Margin="2" local:DataErrorInfoHelper.DataErrorInfo="{Binding}" Commands="{Binding SaveCommand}">Create account</Button>