3

I think I have read every article google returns when I search wpf mvvm-light data validation and I dont know which way to go. I am aware of josh smith, Karl Shifflett's, and MVVM LIGHT's own demo techniques for data validation. What I see is that most validation requires me to fully "re-abstract" my model in my view model. Meaning that I have to create a property in my viewmodel for each property of my model that I want to validate (and in some cases convert all these into string values for binding/validation). This seems like a lot or redundancy when all I want to do is mark most fields as required.

I am using LINQ to entity framework(with self tracking) for my model classes which come from a SQL server DB. As a result I would prefer to keep my business data validation/rules within my viewmodels. I write a simple service interface to get the data from the model and pass it to my viewmodel.

Most of the examples I can find are from as far back as 2008 (ie josh smith). Are these techniques still valid or are there more up to date best practices for mvvm data validation with .NET 4.5 etc.

So I am asking:

1) What methods do you suggest I use 2) What methods work best in a LINQ to EF with MVVM-Light Environment. 3) EDIT: I want to provide feedback to user as they enter data, not just when they submit form

thanks

4

2 回答 2

0

我最终最终使用了以下内容。我将模型更改为使用 LINQ 来自我跟踪实体(有关 STE http://msdn.microsoft.com/en-us/library/vstudio/ff407090%28v=vs.100%29.aspx的信息,请参阅这篇文章)。

LINQ to STE 创建一个实现 iNotifyPropertyChanged 接口的 OnPropertyChanged 事件。

我刚刚为我想要的匹配模型对象(linq 实体生成的代码)创建了一个公共部分类,并为该事件添加了一个事件处理程序OnPropertyChanged。然后,我使用该IDataErrorInfo接口来验证并根据需要抛出错误。这使我可以在字段更改时验证它们并反映给用户。这也允许您执行更高级的验证逻辑,可能需要重新查询数据库(即查找用户名是否已被使用等)或抛出一个对话框

此外,如果我执行绕过 UI 的直接“批处理”操作,在模型中进行数据验证允许我仍然进行验证。

然后我使用HasErrorsandHasChanges属性并使用它们创建一个附加到中继命令的布尔值,如果出现错误,则禁用 crud 命令按钮。

我将发布一些简单的代码来概述我刚刚描述的内容,如果您需要更多详细信息,请发表评论。

这是模型类的实体框架扩展:

 Imports System.ComponentModel


Partial Public Class client

    Implements IDataErrorInfo

#Region "Properties / Declarations"

    'Collection / error description
    Private m_validationErrors As New Dictionary(Of String, String)
    Private _HasChanges As Boolean = False

    ''Marks object as dirty, requires saving
    Public Property HasChanges() As Boolean
        Get
            Return _HasChanges
        End Get
        Set(value As Boolean)
            If Not Equals(_HasChanges, value) Then
                _HasChanges = value
                OnPropertyChanged("HasChanges")
            End If
        End Set
    End Property

    'Extends the class with a property that determines
    'if the instance has validation errors
    Public ReadOnly Property HasErrors() As Boolean
        Get
            Return m_validationErrors.Count > 0
        End Get
    End Property

#End Region

#Region "Base Error Objects"
    'Returns an error message
    'In this case it is a general message, which is
    'returned if the list contains elements of errors
    Public ReadOnly Property [Error] As String Implements System.ComponentModel.IDataErrorInfo.Error
        Get
            If m_validationErrors.Count > 0 Then
                Return "Client data is invalid"
            Else
                Return Nothing
            End If
        End Get
    End Property

    Default Public ReadOnly Property Item(ByVal columnName As String) As String Implements System.ComponentModel.IDataErrorInfo.Item
        Get
            If m_validationErrors.ContainsKey(columnName) Then
                Return m_validationErrors(columnName).ToString
            Else
                Return Nothing
            End If
        End Get
    End Property

#End Region

#Region "Base Error Methods"

    'Adds an error to the collection, if not already present
    'with the same key
    Private Sub AddError(ByVal columnName As String, ByVal msg As String)
        If Not m_validationErrors.ContainsKey(columnName) Then
            m_validationErrors.Add(columnName, msg)
        End If
    End Sub

    'Removes an error from the collection, if present
    Private Sub RemoveError(ByVal columnName As String)
        If m_validationErrors.ContainsKey(columnName) Then
            m_validationErrors.Remove(columnName)
        End If
    End Sub

#End Region

    Public Sub New()

        Me.HasChanges = False
    End Sub

#Region "Data Validation Methods"

    ''handles event and calls function that does the actual validation so that it can be called explicitly for batch processes
    Private Sub ValidateProperty(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Handles Me.PropertyChanged
        If e.PropertyName = "HasChanges" Then
            Exit Sub
        End If
        IsPropertyValid(e.PropertyName)
        HasChanges = True
    End Sub

    Public Function IsPropertyValid(sProperty As String) As Boolean
        Select Case sProperty
            ''add validation by column name here
            Case "chrLast"
                If Me.chrLast.Length < 4 Then
                    Me.AddError("chrLast", "The last name is too short")
                    Return True
                Else
                    Me.RemoveError("chrLast")
                    Return False
                End If
            Case Else
                Return False

        End Select

    End Function

#End Region

End Class

然后在视图模型中我包含以下代码来绑定命令并评估它是否可以执行。

 Public ReadOnly Property SaveCommand() As RelayCommand
        Get
            If _SaveCommand Is Nothing Then
                _SaveCommand = New RelayCommand(AddressOf SaveExecute, AddressOf CanSaveExecute)
            End If
            Return _SaveCommand
        End Get
    End Property

    Private Function CanSaveExecute() As Boolean
        Try
            If Selection.HasErrors = False And Selection.HasChanges = True Then
                Return True
            Else
                Return False
            End If
        Catch ex As Exception
            Return False
        End Try

    End Function

    Private Sub SaveExecute()
        ''this is my LINQ to Self Tracking Entities DataContext
        FTC_Context.SaveChanges()
    End Sub

以下是我绑定按钮的方式(在 WPF 中有自定义样式)

 <Button Content="" Height="40" Style="{DynamicResource ButtonAdd}" Command="{Binding SaveCommand}" Width="40" Cursor="Hand" ToolTip="Save Changes" Margin="0,0,10,10"/>

因此,当没有验证错误并且当前客户端记录“isDirty”时,保存按钮会自动启用,如果这两个条件中的任何一个失败,则会禁用。这样,我现在有一种简单的方法来验证我想要的实体的任何类型的列/数据,并且我可以在用户在表单中输入数据时提供反馈,并且只有在我的所有“条件”都被启用后才启用 CRUD 命令按钮遇见了。

这是一场需要弄清楚的战斗。

于 2013-01-21T09:07:25.013 回答
0

我这样做的方式(不一定正确)是在 ViewModel 中进行验证(通常在其中进行 CRUD 操作),然后如果存在验证错误,则中止保存/添加任何数据并用于Messenger.Default.Send将自定义消息类型发送到我的观点。然后我通过对话框或其他方式提醒用户。

我过去曾尝试过 Binding ValidationRules,但发现迄今为止最可靠和最一致的方法是简单的if语句。

于 2013-01-19T07:28:01.523 回答