【问题标题】:Disable Save button in WPF if validation fails如果验证失败,则禁用 WPF 中的保存按钮
【发布时间】:2010-10-19 22:41:57
【问题描述】:

我采用了似乎是使用 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>

【问题讨论】:

    标签: wpf validation save


    【解决方案1】:

    有几点:

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

    如果您还没有查看 WPF Command 模型,可以获取独家新闻here

    <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(模型-视图-视图模型)设计模式来实现,请查看 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 中,遍历此集合并验证每个 DependencyObject/DepencyProperty 组合,方法是使用 BindingOperation.GetBindingExpression() 获取表达式,然后检查 BindingExpression.HasError

    【讨论】:

    • 非常感谢。不过还有一件事。我可以使用以下代码检查单个控件 If Validation.GetHasError(myTextbox) Then e.CanExecute = False 有没有办法检查所有控件的有效性而不是单独检查?
    • +1 用于建议的命令用法。命令是让 WPF 开始真正为我点击的原因。
    • 喜欢你第一个建议的简单性 Josh。我会试一试。再次感谢
    【解决方案2】:

    我为此创建了附加属性:

    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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-23
      • 1970-01-01
      • 2016-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-10
      相关资源
      最近更新 更多