2014年3月29日土曜日

INotifyPropertyChangeとINotifyDataErrorInfoの実装方法

 .Net技術者であればViewModelを実装するときに必ず書かなくてはいけないINotifyPropertyChangeプロパティ。出来れば書きたくない、.netが勝手にやってくれてもいいんだよと思う部分を少しでも楽にならないかと考えてみた。

まずは、データーフローから考えてみると
値の変更→データチェック→通知
 OK => INotifyPropertyChange
 NG => INotifyDataErrorInfo
という流れで実装する場合、

set {
    if (validation(value)) {
        //INotifyPropertyChange
    }else{
        //INotifyDataErrInfo
    }
}
この辺りは共通処理になるからここをModelBaseクラスという形で実装しておき、
あとはSetProperty関数を用いて上記処理を処理させようというアプローチである。
過去にもModelがらみの記事に書いた
MongoDBと接続、もしくはJSonファイルに出力する方法にも使えるようにしておいた。


実装方法はModelBaseクラス内でNotify関連を実装してそれを任意のModelクラスに継承するやり方。

Imports System.ComponentModel
Imports MongoDB.Driver
Imports MongoDB.Driver.Builders
Imports MongoDB.Driver.Linq
Imports MongoDB.Bson
Imports MongoDB.Bson.Serialization.Attributes

'http://blog.micic.ch/net/easy-mvvm-example-with-inotifypropertychanged-and-inotifydataerrorinfo
Public Class ModelBase : Implements INotifyPropertyChanged, INotifyDataErrorInfo
#Region "data error"
    Private _errors As New Dictionary(Of String, List(Of String))
    Public Event ErrorsChanged(sender As Object, e As DataErrorsChangedEventArgs) Implements INotifyDataErrorInfo.ErrorsChanged
    Public Function GetErrors(propertyName As String) As IEnumerable Implements INotifyDataErrorInfo.GetErrors
        If _errors.ContainsKey(propertyName) Then
            Return _errors(propertyName)
        End If
        Return Nothing
    End Function
    Public ReadOnly Property HasErrors As Boolean Implements INotifyDataErrorInfo.HasErrors
        Get
            Return 0 < _errors.Count
        End Get
    End Property
    Public ReadOnly Property IsValid As Boolean
        Get
            Return Not HasErrors
        End Get
    End Property
    Private Sub AddError(propertyName As String, errmsg As String)
        If Not _errors.ContainsKey(propertyName) Then
            _errors(propertyName) = New List(Of String)
        End If
        If Not _errors(propertyName).Contains(errmsg) Then
            _errors(propertyName).Add(errmsg)
            RaiseEvent ErrorsChanged(Me, New DataErrorsChangedEventArgs(propertyName))
        End If
    End Sub
    Private Sub RemoveError(propertyName As String)
        If _errors.ContainsKey(propertyName) Then
            _errors.Remove(propertyName)
            RaiseEvent ErrorsChanged(Me, New DataErrorsChangedEventArgs(propertyName))
        End If
    End Sub
#End Region
#Region "Property changed"
    Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
#End Region
#Region "Set Property"
    Private _lock As New Object()
    Protected Delegate Function DlgCheckValidation() As String
    Protected Sub SetProperty(Of T)(propertyName As String, ByRef field As T, value As T, func As DlgCheckValidation)
        If Not EqualityComparer(Of T).Default.Equals(field, value) Then
            SyncLock _lock
                Dim errmsg = ""
                If Not IsNothing(func) Then
                    errmsg = func()
                End If
                If "" = errmsg Then
                    field = value
                    RemoveError(propertyName)
                    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
                Else
                    AddError(propertyName, errmsg)
                End If
            End SyncLock
        End If

    End Sub
#End Region
End Class



<BsonIgnoreExtraElements>
Public Class AddressModel : Inherits ModelBase
    <BsonId>
    <DisplayName("ID")>
    Public Property _id As BsonObjectId

    Private _name As String
    Private _birthday As Date
    Private _emailAddress As List(Of EMailModel)

    <DisplayName("名前")>
    Public Property name As String
        Get
            Return _name
        End Get
        Set(value As String)
            SetProperty("name", _name, value,
                Function() As String
                        If "abc" = value Then
                            Return "abc not allowed"
                        End If
                        Return ""
                    End Function)
        End Set
    End Property

    Public Property Birthday As Date
        Get
            Return _birthday
        End Get
        Set(value As Date)
            SetProperty("Birthday", _birthday, value, Nothing)
        End Set
    End Property
    Public Property EMailAddress As List(Of EMailModel)
        Get
            Return _emailAddress
        End Get
        Set(value As List(Of EMailModel))
            SetProperty("EMailAddress", _emailAddress, value, Nothing)
        End Set
    End Property

End Class

<BsonIgnoreExtraElements>
Public Class EMailModel : Inherits ModelBase
    Private _DisplayName As String
    Private _MailAddress As String

    Public Property DisplayName As String
        Get
            Return _DisplayName
        End Get
        Set(value As String)
            SetProperty("DisplayName", _DisplayName, value, Nothing)
        End Set
    End Property
    Public Property MailAddress As String
        Get
            Return _MailAddress
        End Get
        Set(value As String)
            SetProperty("MailAddress", _MailAddress, value, Nothing)
        End Set
    End Property
End Class




参照サイト:
http://ivis-mynikki.blogspot.jp/2013/01/net-or.htmlhttp://ivis-mynikki.blogspot.jp/2014/03/blog-post.html
http://blog.micic.ch/net/easy-mvvm-example-with-inotifypropertychanged-and-inotifydataerrorinfo
 
 
 

0 件のコメント:

Androider