Home > Articles > Programming > Windows Programming

  • Print
  • + Share This
From the author of Creating a Library to Expose Models and ViewModels

Creating a Library to Expose Models and ViewModels

The first part of the example is made of a portable class library that exposes a list of customers, as well as the objects to work with such a list. Create a new project of type Portable Class Library called CustomersLibrary, leaving the default selection of target platforms unchanged. Once the project is created, remove the Class1.vb (or Class1.cs if you use Visual C#) and add a new class called Customer. Listing 1 shows the code of the new class.

Listing 1—Implementing the Customer class.

Imports System.Collections.ObjectModel
Imports System.Windows.Input
Imports System.ComponentModel

Public Class Customer
    Public Property CustomerID() As Integer
    Public Property CompanyName() As String
    Public Property EmailAddress As String
    Public Property PhysicalAddress As String
    Public Property Phone() As String
End Class

The Customer class is a simplified representation of customer information. You don't need detailed information at this point; keeping things simple allows for writing cleaner and easier code.

Next, you need a collection that holds a list of customers. All the targeted platforms support the ObservableCollection class, which is appropriate for data-binding scenarios. Listing 2 shows how to implement a collection of customers, also generating some sample data when the collection is instantiated.

Listing 2—Defining a collection of customers using the ObservableCollection class.

Public Class Customers
    Private _customers As ObservableCollection(Of Customer)

    Public Sub New()
        Me._customers = New ObservableCollection(Of Customer)
        'Create some sample data
        Me._customers.Add(New Customer With {.CustomerID = 1, .CompanyName = "A Fabulous Company",
                                           .EmailAddress = _
                                            "fable@fabulouscompany.com",
                                           .Phone = "111-111-1111",
                                           .PhysicalAddress = "Seattle, WA"})

        Me._customers.Add(New Customer With {.CustomerID = 2, .CompanyName = "RM Consulting",
                                           .EmailAddress = _
                                           "rm@rmconsulting.it",
                                           .Phone = "222-111-1111",
                                           .PhysicalAddress = "Varese, Italy"})

        Me._customers.Add(New Customer With {.CustomerID = 3, .CompanyName = "Northwind Traders",
                                           .EmailAddress = _
                                           "nwind@northwindtraders.com",
                                           .Phone = "222-222-1111",
                                           .PhysicalAddress = "San Francisco, CA"})

    End Sub

    'Return the full list of customers
    Public Function GetCustomers() As ObservableCollection(Of Customer)
        Return Me._customers
    End Function

    'Remove the specified customer from the collection
    Public Sub DeleteCustomer(currentCustomer As Customer)
        Me._customers.Remove(currentCustomer)
    End Sub

End Class

Methods of this class won't be used directly by client applications; instead, they're invoked by ViewModels. Most of the power of the MVVM pattern relies on the ICommand interface. This object represents a command that executes an action and that is data-bound to controls of the user interface. With this approach, your clients will invoke commands rather than handling click events, making the user interface totally independent from imperative code.

In the MVVM pattern, we need to define a class that implements the ICommand interface and relays requests about command execution and about checks on the command state. This class then sends back to the ViewModel the request of actually executing commands. This class, known as RelayCommand, is shown in Listing 3. The RelayCommand class provides a reusable command infrastructure and helps developers keep ViewModels simpler.

Listing 3—Defining the RelayCommand class.

Public Class RelayCommand
    Implements ICommand

    Private _isEnabled As Boolean
    Private ReadOnly _handler As Action

    Public Sub New(ByVal handler As Action)
        _handler = handler
    End Sub

    Public Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged

    Public Property IsEnabled() As Boolean
        Get
            Return _isEnabled
        End Get
        Set(ByVal value As Boolean)
            If (value <> _isEnabled) Then
                _isEnabled = value
                RaiseEvent CanExecuteChanged(Me, EventArgs.Empty)
            End If
        End Set
    End Property

    Public Function CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute
        Return IsEnabled
    End Function

    Public Sub Execute(parameter As Object) Implements ICommand.Execute
        _handler()
    End Sub
End Class

The next step is defining a base, reusable ViewModel so that specialized ViewModels can inherit from that base version. In this case, the base ViewModel only provides the infrastructure for change notifications over data, as demonstrated in Listing 4.

Listing 4—Defining a base ViewModel.

'Generic ViewModel
Public MustInherit Class ViewModelBase
    Implements INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler Implements
           INotifyPropertyChanged.PropertyChanged

    'Raise a property change notification
    Protected Overridable Sub OnPropertyChanged(propname As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propname))
    End Sub
End Class

Another benefit of the portable class library is that it supports change notifications through the INotifyPropertyChanged interface implementation. This implies that the data-binding engine in WPF, the Windows Runtime, Windows Phone, and Silverlight works similarly and raises change notifications in the same way.

The final step for the portable class library is implementing a ViewModel that exposes to callers the customer information and commands to manage customers. Listing 5 provides this implementation.

Listing 5—Implementing a view-model for customers information.

Public Class CustomerViewModel
    Inherits ViewModelBase

    Private _customers As ObservableCollection(Of Customer)
    Private _currentCustomer As Customer
    Private _customersList As Customers
    Private _deleteCustomerCommand As RelayCommand

    Public Sub New()
        Me._customersList = New Customers
        Me._customers = Me._customersList.GetCustomers

        SetupCommands()
    End Sub

    'Set up commands by creating instances of RelayCommand
    'and passing the appropriate delegate
    Private Sub SetupCommands()
        DeleteCustomerCommand = New RelayCommand(AddressOf DeleteCustomer)
    End Sub

    'Expose the command
    Public Property DeleteCustomerCommand() As RelayCommand
        Get
            Return _deleteCustomerCommand
        End Get
        Private Set(value As RelayCommand)
            _deleteCustomerCommand = value
        End Set
    End Property

    'Expose the list of contacts
    Public Property Customers() As ObservableCollection(Of Customer)
        Get
            Return Me._customers
        End Get
        Set(value As ObservableCollection(Of Customer))
            Me._customers = value
        End Set
    End Property

    'Represents the currently selected Customer
    'Raise a property change notification
    'Enable the command
    Public Property CurrentCustomer() As Customer
        Get
            Return _currentCustomer
        End Get
        Set(value As Customer)
            _currentCustomer = value
            OnPropertyChanged("CurrentCustomer")
            DeleteCustomerCommand.IsEnabled = True
        End Set
    End Property

    'Delete the specified Customer
    Public Sub DeleteCustomer()
        Me._customersList.DeleteCustomer(CurrentCustomer)
    End Sub

End Class

Notice that the list of customers is exposed as an ObservableCollection, which is the most appropriate way to expose data to XAML callers. Also, notice the availability of the CurrentCustomer property, which is also used to synchronize the current customer in the collection with the instance of the customer inside controls of the user interface.

At this point, you can compile the library and ensure that you get no errors.

  • + Share This
  • 🔖 Save To Your Account