MVVM in VB.Net - Lesson 4: The Model

Overview

The Model is a group of classes, either object classes or ADO.Net, that forms a data structure. From there, you can export the data in either XML or binary format. Furthermore, you can use the model layer to communicate with an external data, whether it’s local, remote, or even a web service.

You can hold data in the Model. If you have a large amount of data, you may want to use a database, and have the model connect to retrieve and save data.

The following classes will be placed in the Model folder.

ModelBase

The ModelBase is an abstract class which all Model classes will inherit from. This model class inherits the ObserverableDataObject and implements the IEditableObject interface. With this interface, we can allow the user to accept the valid data, reset data, or cancel the edit altogether..

ModelBase.vb

Namespace Base

    ''' <summary>
    ''' This is the abstract base class for AdvancedModels. 
    ''' Advanced Models can reset as well as validate.
    ''' Use AdvancedWorkspaceViewModel to inject this model into the view.
    ''' </summary>
    Public MustInherit Class ModelBase
        Implements ComponentModel.IEditableObject
        Implements ComponentModel.IChangeTracking


        ''' <summary>
        ''' Begins the editing session.
        ''' </summary>
        Public Sub BeginEdit() Implements System.ComponentModel.IEditableObject.BeginEdit
            BackupData()
            AcceptChanges()
            IsInEdit = True
        End Sub

        ''' <summary>
        ''' Cancels the editing session.
        ''' </summary>
        Public Sub CancelEdit() Implements System.ComponentModel.IEditableObject.CancelEdit
            RestoreData()
            AcceptChanges()
            IsInEdit = False
            ClearBackup()
        End Sub

        ''' <summary>
        ''' Save the data and ends the editing session.
        ''' </summary>
        Public Sub EndEdit() Implements System.ComponentModel.IEditableObject.EndEdit
            ClearBackup()
            AcceptChanges()
            IsInEdit = False
            ClearBackup()
        End Sub

        ''' <summary>
        ''' Resets the Model.
        ''' </summary>
        Public Sub Reset()
            EndEdit()
            BeginEdit()
        End Sub

        ''' <summary>
        ''' Resets the backup structure to it's original state.
        ''' </summary>
        Protected MustOverride Sub ClearBackup()

        ''' <summary>
        ''' Restores properties from backup.
        ''' </summary>
        Protected MustOverride Sub RestoreData()

        ''' <summary>
        ''' Fills backup from properties.
        ''' </summary>
        Protected MustOverride Sub BackupData()

#Region "IsChageTracking Implementation"

        ''' <summary>
        ''' Local storage.
        ''' </summary>
        Private Changes As New List(Of String)

        ''' <summary>
        ''' Adds a column that has changed from the original data.
        ''' </summary>
        ''' <param name="columnName">The column name.</param>
        Protected Sub AddChange(ByVal columnName As String)
            If Not Changes.Contains(columnName) Then
                Changes.Add(columnName)
            End If
        End Sub


        ''' <summary>
        ''' Removes a column that is the same as the original data.
        ''' </summary>
        ''' <param name="columnName">The column name.</param>
        Protected Sub RemoveChange(ByVal columnName As String)
            If Changes IsNot Nothing AndAlso Changes.Contains(columnName) Then
                Changes.Remove(columnName)
            End If
        End Sub

        ''' <summary>
        ''' Clears the changes, and notifies that the data is clean.
        ''' </summary>
        Private Sub AcceptChanges() Implements System.ComponentModel.IChangeTracking.AcceptChanges
            Changes.Clear()
        End Sub


        ''' <summary>
        ''' Exposes a Boolean value to the View specifying the data has changes and should be saved.
        ''' You can use this property to toggle a reset button.
        ''' </summary>
        ''' <value>Boolean</value>
        ''' <returns>True if the data has changes; otherwise false.</returns>
        Public ReadOnly Property IsChanged As Boolean Implements System.ComponentModel.IChangeTracking.IsChanged
            Get
                Return (Changes.Count > 0)
            End Get
        End Property

#End Region

        ''' <summary>
        ''' Use this method after the data is loaded into the properties.
        ''' </summary>
        Public Overrides Sub InitializeData(Optional UseNeedsValidationAttribute As Boolean = False)
            Dim info() As Reflection.PropertyInfo = Me.GetType.GetProperties
            For Each pi In info
                If UseNeedsValidationAttribute Then
                    Dim att As Attributes.NeedsValidationAttribute = CType(pi.GetCustomAttributes(GetType(Attributes.NeedsValidationAttribute), False).FirstOrDefault, NeedsValidationAttribute)

                    If att IsNot Nothing Then
                        If pi.CanRead Then
                            Dim o As Object = pi.GetValue(Me, Nothing)
                            If pi.CanWrite Then
                                pi.SetValue(Me, o, Nothing)
                            End If
                        End If
                    End If
                Else
                    If pi.CanRead AndAlso pi.CanWrite Then
                        pi.SetValue(Me, pi.GetValue(Me, Nothing), Nothing)
                    End If
                End If
            Next
        End Sub


    End Class


End Namespace

CustomerModel

The CustomerModel class will be used to hold our customer’s data. We will place a structure in the class. This structure will be our backup. So if the user cancels the data edit, we will be able to reset the exposed properties. We, of course, will inherit the ModelBase Class.

CustomerModel.vb

<Serializable()> _
Public Class Customer
    Inherits ModelBase

#Region "Backup"

    Private Structure struct_Backup
        Dim FirstName As String
        Dim LastName As String
    End Structure
    ' We don't want to save the backup. This is just here for the properties.
    <NonSerialized()> Private Backup As New struct_Backup

#End Region

#Region "Properties"
    ' Place Properties here.


    Private _FirstName As String
    Public Property FirstName() As String
        Get
            Return _FirstName
        End Get
        Set(ByVal value As String)
            _FirstName = value

            If value <> Backup.FirstName Then
                AddChange("FirstName")
            Else
                RemoveChange("FirstName")
            End If

            OnPropertyChanged("FirstName")
        End Set
    End Property


    Private _LastName As String
    Public Property LastName() As String
        Get
            Return _LastName
        End Get
        Set(ByVal value As String)
            _LastName = value

            If value <> Backup.LastName Then
                AddChange("LastName")
            Else
                RemoveChange("LastName")
            End If

            ' Data Error
            If value Is Nothing OrElse value.Trim.Length = 0 Then
                AddError("LastName", "The last name cannot be left blank")
            Else
                RemoveError("LastName")
            End If

            OnPropertyChanged("LastName")
        End Set
    End Property


#End Region

#Region "Backup Methods"

    Protected Overrides Sub ClearBackup()
        ' Reset the Backup structure to clear it from memory
        Backup = New struct_Backup
    End Sub

    Protected Overrides Sub RestoreData()
        ' Pull from backup and place in properties.
        With Backup
            FirstName = .FirstName
            LastName = .LastName
        End With
    End Sub

    Protected Overrides Sub BackupData()
        ' Place the property values in the backup.
        With Backup
            .LastName = _LastName
            .FirstName = _FirstName
        End With
    End Sub


#End Region

End Class

Notice the lines added to the properties:

OnPropertyChanged - From the ObserverableObject class. It is used to notify the View that this property has changed, and it needs to update its data.

I've placed a Data Error in the LastName Property's Set method:

If value Is Nothing OrElse value.Trim.Length = 0 Then 
     AddError("LastName", "The last name is required") 
Else 
     RemoveError("LastName") 
End If


The code above tells the UI that if the value is nothing (Null) or if the length of the value is 0 that it has an error in it’s data. When we finish the project, you will see a red border around the text box. This is WPF’s default data error template.

Furthermore, the data error uses a dictionary to keep track of the data errors. If there is an error, the save command in the ViewModel is disabled and will not fire. This way, the data update cannot complete with errors.

NOTE: The Model classes are marked Serializable.

More by this Author


Comments 17 comments

Ingo 5 years ago

Very nice Lessons. I am very glad to find some MVVM-explanations related to vb.net. I do not undersand the customer-class. What does the code look like?


Maligui profile image

Maligui 5 years ago from Elkhart, IN USA Author

Thank you for posting that. I had the code field entered into the post, but didn't fill in the code. Must have been a long day :).


frank 5 years ago

i did not understand the customer class. I get a lot of errors. At first you use a namespace (Core.Base.ModelBase) but i never have seen a namespace in this project and otherwise the functions AddChange, RemoveChange does not exist.

Can you complete this tutorial, thath will be very nice,

thank you

Frank


Maligui profile image

Maligui 5 years ago from Elkhart, IN USA Author

I have updated the CustomerModel.vb. I have the layers kinda messed up. I am working an another tutorial to create an MVVM application using multiple projects.

All is well again (:.

- Ryan


Dave 5 years ago

Hi Maligui,

Nice tutorial, you're great but i can't see your changes in CustomerModel.vb.

Thanks for advance.

Dave


Maligui profile image

Maligui 5 years ago from Elkhart, IN USA Author

The CustomerModel.vb was inheriting from a diffeent namespace, but it was in the same Namespace as it's base class. That really is the only change. Visitors were getting an error from the class that wasn't allowing to them continue.


David 4 years ago

Hi Maligui,

The ModelBase class contains the following methods that must be overriden:

Public MustOverride Sub BeginEdit() Implements System.ComponentModel.IEditableObject.BeginEdit

Public MustOverride Sub CancelEdit() Implements System.ComponentModel.IEditableObject.CancelEdit

Public MustOverride Sub EndEdit() Implements System.ComponentModel.IEditableObject.EndEdit

however I can't see any methods in the CustomerModel class that defines these methods. What do you do for each of these methods?

Kind regards,

Dave.


Maligui profile image

Maligui 4 years ago from Elkhart, IN USA Author

Hi Dave, the BeginEdit method is used to backup the property values into a structure. CancelEdit pulls the values from the structure back into the properties. EndEdit could be used to raise an event notifying that the data needs to be saved.


Microshine 4 years ago

I tried to copy your code to my project. But I have some errors. Methods AddChange and RemoveChang are not declare.


Maligui profile image

Maligui 4 years ago from Elkhart, IN USA Author

I have updated the ObservableDataObject class to implement IChangeTracking. The CustomerClass is calling the methods that are implemented in that class.


Edmund 3 years ago

Public MustInherit Class ModelBase

Inherits ModelBase

with the following error:

Class 'ModelBase' cannot inherit from itself: 'ModelBase' inherits from 'ModelBase'.


Erik 3 years ago

There seems to be an error in ModelBase.vb. I think the line

'Implements ModelBase'

should be replaced with:

'Implements ObservableDataObject'


Maligui profile image

Maligui 3 years ago from Elkhart, IN USA Author

My mistake. This error was corrected.


Barnman 2 years ago

Hi Maligui

The code for modelbase has errors - Three cases of IsInEdit is not declared. You say it inherits from ObservableDataObject but it does not.

Adding this did not help though. Summary says it is for the Advanced Model - is it the wrong file altogether?


Nemicio 2 years ago

Hi Maligui,

May you share code Crud use MVVM connect to mysql user VB.Net.

Need Your Help...

Nemicio


Jeremy 15 months ago

Hello,

Like some others, I notice that the ModelBase class does not (as written) inherit from ObservableDataObject. Like above, it's noted that a few methods have not been implemented in the Base Class (ObservableDataObject or ModelBase) for RestoreData()


TroyThompson1 8 months ago

Hi Maligui,

Great organization and explanation on these.

I'm getting 2 errors on the Model Base (I'm in VS 2013 and .Net 4.5):

IsInEdit is not found - I could create a local variable easy enough but thought it may be from an earlier .Net framework object (IChangeTracking/IEditableObject) and want to resolve that if its the case.

Custom Attribute 'Attributes.NeedsValidationAttribute' - Haven't done much with custom attributes. I'm assuming I would add any of these to the Core? I'm looking into the how-to's of this now...

Thx in advance all for any help...

    Sign in or sign up and post using a HubPages Network account.

    0 of 8192 characters used
    Post Comment

    No HTML is allowed in comments, but URLs will be hyperlinked. Comments are not for promoting your articles or other sites.


    Click to Rate This Article
    working