如果WPF验证失败,则禁用保存按钮。

21

我采用了似乎是WPF中使用IDataErrorInfo接口和样式验证文本框的标准方式,如下所示。 但是,当页面无效时,如何禁用“保存”按钮? 这是通过触发器以某种方式完成的吗?

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>
2个回答

40

几点建议:

首先,我建议使用RoutedCommand ApplicationCommands.Save来实现保存按钮的处理。

如果您还没有了解过WPF命令模型,您可以在这里获取更多信息:这里

<Button Content="Save" Command="Save">

现在,要实现这个功能,你可以将命令绑定添加到Window/UserControl或Button本身:

    <Button.CommandBindings>
        <CommandBinding Command="Save" 
                        Executed="Save_Executed" CanExecute="Save_CanExecute"/>
    </Button.CommandBindings>
</Button>

在代码后端实现这些功能:

private void Save_Executed(object sender, ExecutedRoutedEventArgs e)
{
}

private void Save_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
Save_CanExecute中,根据文本框上绑定的有效性设置e.CanExecute
如果要使用MVVM(Model-View-ViewModel)设计模式实现,请查看Josh Smith在CommandSinkBinding上的文章。
最后注意:如果希望在TextBox中的值更改时立即更新启用/禁用状态,请在TextBox的绑定上设置UpdateSourceTrigger="PropertyChanged"
编辑:如果要基于控件中的所有绑定进行验证/无效,请尝试以下几个建议。
1)已经在实现IDataErrorInfo。尝试实现IDataErrorInfo.Error属性,使其返回对于您绑定到的所有属性都无效的字符串。这仅适用于整个控件绑定到单个数据对象的情况。设置e.CanExecute = string.IsNullOrEmpty(data.Error); 2)使用反射获取相关控件上的所有公共静态DependencyProperties。然后在循环中调用BindingOperations.GetBindingExpression(relevantControl, DependencyProperty)以便测试验证。
3)在构造函数中,手动创建所有嵌套控件上绑定属性的集合。在CanExecute中,通过使用BindingOperation.GetBindingExpression()来获取表达式并检查BindingExpression.HasError,迭代此集合并验证每个DependencyObject/DepencyProperty组合。

1
非常好用,感谢。不过还有一件事。我可以使用以下代码检查单个控件: If Validation.GetHasError(myTextbox) Then e.CanExecute = False 是否有一种方法可以检查所有控件的有效性而不是单个控件? - Mitch
2
命令是让WPF开始真正易用的关键。建议使用命令。 - Greg D
喜欢你的第一个建议Josh的简洁性。我会试一试。再次感谢。 - Mitch

1

我为此创建了附加属性:

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);
        }
    }
}

我在表单上这样使用它:
<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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接