C# MVVM 模式、IDataErrorInfo 和绑定显示错误?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1123062/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 08:44:58  来源:igfitidea点击:

MVVM pattern, IDataErrorInfo and Binding to display error?

c#.netwpfmvvmbinding

提问by Patrick Desjardins

On MSDN Magazineit has a good article about MVVM and they are binding the validation error in the Xaml to Validation.ErrorTemplate="{x:Null}". I do not get it why and how they can display from the IDataErrorInfo the error? Anyone can light me on how to get the error message displayed to the screen with the MVVM approach?

MSDN 杂志上有一篇关于 MVVM 的好文章,他们将 Xaml 中的验证错误绑定到Validation.ErrorTemplate="{x:Null}". 我不明白为什么以及如何从 IDataErrorInfo 显示错误?任何人都可以告诉我如何使用 MVVM 方法将错误消息显示在屏幕上?

采纳答案by aku

I was looking at the same sample just a few minutes ago. Your guess is righ. In this code sample they removed default ErrorTemplate from TextBox control so it would't show red rectangle. Instead of using ErrorTemplate they create ContentProvider with content bound to validation error of specific text box.

几分钟前我还在看同一个样本。你的猜测是对的。在此代码示例中,他们从 TextBox 控件中删除了默认的 ErrorTemplate,因此它不会显示红色矩形。他们不使用 ErrorTemplate,而是使用绑定到特定文本框的验证错误的内容创建 ContentProvider。

回答by Patrick Desjardins

From what I am guessing it's that Validation.ErrorTemplate="{x:Null}" remove the red rectangle when an error is present. This might be set to be able to not have the red rectangle around the textbox when the form start.

根据我的猜测,Validation.ErrorTemplate="{x:Null}" 会在出现错误时删除红色矩形。这可能被设置为在表单启动时文本框周围没有红色矩形。

For the error show to the form, I saw somewhere in the code : Content="{Binding ElementName=lastNameTxt, Path=(Validation.Errors).CurrentItem}", so, I am still guessing that it's Bind to the textbox (not the data model) and check if it has error with a static Validation.Errors that might connect to the IDataErroInfo from the data model?

对于表单的错误显示,我在代码中的某处看到了 : Content="{Binding ElementName=lastNameTxt, Path=(Validation.Errors).CurrentItem}",所以,我仍然猜测它绑定到文本框(而不是数据模型)并检查它是否有可能连接到的静态 Validation.Errors 错误来自数据模型的 IDataErroInfo?

Am I guessing right?

我猜对了吗?

回答by Mark

When you bind to an object that supports IDataErrorInfo, there are several features of the WPF Binding class to consider:

绑定到支持 IDataErrorInfo 的对象时,需要考虑 WPF Binding 类的几个功能:

  1. ValidatesOnDataErrors must be True. This instructs WPF to look for and use the IDataError interface on the underlying object.

  2. The attached property Validation.HasError will be set to true on the target object if the source object's IDataError interface reported a validation problem. You can then use this property with trigger to change the tooltip of the control to display the validation error message (I'm doing this in my current project and the end user's love it).

  3. The Validation.Errors attached property will contain an enumeration of any ValidationResult errors resulting from the last validation attempt. If you're going with the tooltip approach, use an IValueConverter to retrieve only the first item... otherwise you run into binding errors for displaying the error message itself.

  4. The binding class exposes NotifyOnValidationError, which when True, will cause routed events to bubble up from the bound control every time a validation rule's state changes. This is useful if you want to implement an event handler in the container of the bound controls, and then add and remove the validation messages to/from a listbox.

  1. ValidatesOnDataErrors 必须为 True。这将指示 WPF 在基础对象上查找和使用 IDataError 接口。

  2. 如果源对象的 IDataError 接口报告了验证问题,则附加属性 Validation.HasError 将在目标对象上设置为 true。然后,您可以将此属性与触发器一起使用来更改控件的工具提示以显示验证错误消息(我在当前项目中这样做并且最终用户很喜欢它)。

  3. Validation.Errors 附加属性将包含上次验证尝试导致的任何 ValidationResult 错误的枚举。如果您要使用工具提示方法,请使用 IValueConverter 仅检索第一项……否则您会遇到绑定错误以显示错误消息本身。

  4. 绑定类公开 NotifyOnValidationError,当它为 True 时,每次验证规则的状态更改时,都会导致路由事件从绑定控件冒泡。如果您想在绑定控件的容器中实现事件处理程序,然后向/从列表框添加和删除验证消息,这将非常有用。

There are samples on MSDN for doing both style of feedback (the tooltips as well as the listbox), but I'll paste below the code I roled to implement the tooltip feedback on my DataGridCells and TextBoxes...

MSDN 上有用于执行两种反馈风格(工具提示和列表框)的示例,但我将粘贴在我用来在我的 DataGridCells 和 TextBox 上实现工具提示反馈的代码下方...

The DataGridCell style:

DataGridCell 样式:

   <Style TargetType="{x:Type dg:DataGridCell}"
           x:Key="DataGridCellStyle">

      <Setter Property="ToolTip"
              Value="{Binding Path=Column.(ToolTipService.ToolTip),RelativeSource={RelativeSource Self}}" />

      <Style.Triggers>
        <Trigger Property="Validation.HasError"
                 Value="True">
          <Setter Property="ToolTip"
                  Value="{Binding RelativeSource={RelativeSource Self},Path=(Validation.Errors), Converter={StaticResource ErrorContentConverter}}" />
        </Trigger>
      </Style.Triggers>

    </Style>

The TextBox style:

文本框样式:

     <Style x:Key="ValidatableTextBoxStyle" TargetType="TextBox">
  <!--When the control is not in error, set the tooltip to match the AutomationProperties.HelpText attached property-->
  <Setter Property="ToolTip"
          Value="{Binding RelativeSource={RelativeSource Mode=Self},Path=(AutomationProperties.HelpText)}" />

          <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="true">
              <Setter Property="ToolTip"
                      Value="{Binding RelativeSource={x:Static RelativeSource.Self},Path=(Validation.Errors)[0].ErrorContent}" />
            </Trigger>
          </Style.Triggers>
        </Style>

The ErrorContentConverter (for retrieving the first validation error message for the tooltip):

ErrorContentConverter(用于检索工具提示的第一条验证错误消息):

Imports System.Collections.ObjectModel

Namespace Converters

    <ValueConversion(GetType(ReadOnlyObservableCollection(Of ValidationError)), GetType(String))> _
    Public Class ErrorContentConverter
        Implements IValueConverter

        Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
            Dim errors As ReadOnlyObservableCollection(Of ValidationError) = TryCast(value, ReadOnlyObservableCollection(Of ValidationError))
            If errors IsNot Nothing Then
                If errors.Count > 0 Then
                    Return errors(0).ErrorContent
                End If
            End If
            Return String.Empty
        End Function

        Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
            Throw New NotImplementedException()
        End Function

    End Class

End Namespace

...and finally an example of using the style in a textbox:

...最后是在文本框中使用样式的示例:

    <TextBox Text="{Binding Path=EstimatedUnits,ValidatesOnDataErrors=True,NotifyOnValidationError=True}"
             Style="{StaticResource ValidatableTextBoxStyle}"
             AutomationProperties.HelpText="The number of units which are likely to sell in 1 year." />

回答by Sai

Here is code I used for both displaying the error in Tooltip Or A small bubble next to the control.

这是我用于在工具提示或控件旁边的小气泡中显示错误的代码。

  1. Define the Style.

    <Style x:Key="TextBoxValidationStyle" TargetType="{x:Type TextBox}">
        <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>
    
    <Style x:Key="TextboxErrorBubbleStyle" TargetType="{x:Type TextBox}" BasedOn="{StaticResource ResourceKey=TextBoxValidationStyle}">
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <DockPanel LastChildFill="true">
    
                        <Border Background="Red" DockPanel.Dock="right" Margin="5,0,0,0" Width="10" Height="10" CornerRadius="10"
                            ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
    
                            <TextBlock Text="!" VerticalAlignment="center" HorizontalAlignment="center" FontWeight="Bold" Foreground="white"/>
                        </Border>
    
                        <AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >
                            <Border BorderBrush="red" BorderThickness="1" />
                        </AdornedElementPlaceholder>
    
                    </DockPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

  2. Using with Control.

    <TextBox Text="{Binding Path=FirstName, Mode=TwoWay, ValidatesOnDataErrors=True,NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged}"
                Style="{StaticResource TextBoxValidationStyle}" Width="100" Margin="3 5 3 5"/>
     <TextBox Text="{Binding Path=LastName, Mode=TwoWay, ValidatesOnDataErrors=True,NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged}"
                Style="{StaticResource TextboxErrorBubbleStyle}" Width="100" Margin="0 5 3 5"/>
    

  3. Sample Model Class.

    class Customer : INotifyPropertyChanged, IDataErrorInfo { private string firstName; private string lastName;

    public string FirstName
    {
        get { return firstName; }
        set
        {
            if (firstName != value)
            {
                firstName = value;
                RaisePropertyChanged("FirstName");
            }
        }
    }
    
    public string LastName
    {
        get { return lastName; }
        set
        {
            if (lastName != value)
            {
                lastName = value;
                RaisePropertyChanged("LastName");
            }
        }
    }
    public string Error
    {
        get { throw new System.NotImplementedException(); }
    }
    
    public string this[string columnName]
    {
        get
        {
            string message = null;
            if (columnName == "FirstName" && string.IsNullOrEmpty(FirstName))
            {
                message = "Please enter FirstName";
            }
            if (columnName == "LastName" && string.IsNullOrEmpty(LastName))
            {
                message = "Please enter LastName";
            }
            return message;
        }
    }
    

    }

  4. UI Look and feel.

  1. 定义样式。

    <Style x:Key="TextBoxValidationStyle" TargetType="{x:Type TextBox}">
        <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>
    
    <Style x:Key="TextboxErrorBubbleStyle" TargetType="{x:Type TextBox}" BasedOn="{StaticResource ResourceKey=TextBoxValidationStyle}">
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <DockPanel LastChildFill="true">
    
                        <Border Background="Red" DockPanel.Dock="right" Margin="5,0,0,0" Width="10" Height="10" CornerRadius="10"
                            ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
    
                            <TextBlock Text="!" VerticalAlignment="center" HorizontalAlignment="center" FontWeight="Bold" Foreground="white"/>
                        </Border>
    
                        <AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >
                            <Border BorderBrush="red" BorderThickness="1" />
                        </AdornedElementPlaceholder>
    
                    </DockPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

  2. 与控制一起使用。

    <TextBox Text="{Binding Path=FirstName, Mode=TwoWay, ValidatesOnDataErrors=True,NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged}"
                Style="{StaticResource TextBoxValidationStyle}" Width="100" Margin="3 5 3 5"/>
     <TextBox Text="{Binding Path=LastName, Mode=TwoWay, ValidatesOnDataErrors=True,NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged}"
                Style="{StaticResource TextboxErrorBubbleStyle}" Width="100" Margin="0 5 3 5"/>
    

  3. 示例模型类。

    类客户:INotifyPropertyChanged,IDataErrorInfo { 私有字符串 firstName;私人字符串姓氏;

    public string FirstName
    {
        get { return firstName; }
        set
        {
            if (firstName != value)
            {
                firstName = value;
                RaisePropertyChanged("FirstName");
            }
        }
    }
    
    public string LastName
    {
        get { return lastName; }
        set
        {
            if (lastName != value)
            {
                lastName = value;
                RaisePropertyChanged("LastName");
            }
        }
    }
    public string Error
    {
        get { throw new System.NotImplementedException(); }
    }
    
    public string this[string columnName]
    {
        get
        {
            string message = null;
            if (columnName == "FirstName" && string.IsNullOrEmpty(FirstName))
            {
                message = "Please enter FirstName";
            }
            if (columnName == "LastName" && string.IsNullOrEmpty(LastName))
            {
                message = "Please enter LastName";
            }
            return message;
        }
    }
    

    }

  4. 用户界面外观和感觉。

enter image description here

在此处输入图片说明