Using Portable Class Library with Model-View-View Model
You can use the .NET Framework Portable Class Library to implement the Model-View-View Model (MVVM) pattern and share assemblies across multiple platforms.
MVVM is an application pattern that isolates the user interface from the underlying business logic. You can implement the model and view model classes in a Portable Class Library project in Visual Studio 2012, and then create views that are customized for different platforms. This approach enables you to write the data model and business logic only once, and use that code from .NET Framework, Silverlight, Windows Phone, and Windows Store apps, as shown in the following illustration.
This topic does not provide general information about the MVVM pattern. It only provides information about how to use Portable Class Library to implement MVVM. For more information about MVVM, see the MVVM Quickstart in the MSDN Library.
When you target the .NET Framework 4.5, .NET for Windows Store apps, Silverlight, or Windows Phone 7.5 for your Portable Class Library project, the following classes are available for implementing the MVVM pattern:
-
System.Collections.ObjectModel.ObservableCollection(Of T) class
-
System.Collections.ObjectModel.ReadOnlyObservableCollection(Of T) class
-
System.Collections.Specialized.INotifyCollectionChanged class
-
System.Collections.Specialized.NotifyCollectionChangedAction class
-
System.Collections.Specialized.NotifyCollectionChangedEventArgs class
-
System.Collections.Specialized.NotifyCollectionChangedEventHandler class
-
All classes in the System.ComponentModel.DataAnnotations namespace
To implement MVVM, you typically create both the model and the view model in a Portable Class Library project, because a Portable Class Library project cannot reference a non-portable project. The model and view model can be in the same project or in separate projects. If you use separate projects, add a reference from the view model project to the model project.
After you compile the model and view model projects, you reference those assemblies in the app that contains the view. If the view interacts only with the view model, you only have to reference the assembly that contains the view model.
The following example shows a simplified model class that could reside in a Portable Class Library project.
The following example shows a simple way to populate, retrieve, and update the data in a Portable Class Library project. In a real app, you would retrieve the data from a source such as a Windows Communication Foundation (WCF) service.
Namespace SimpleMVVM.Model Public Class CustomerRepository Private _customers As List(Of Customer) Public Sub New() _customers = New List(Of Customer) From { New Customer() With {.CustomerID = 1, .FullName = "Dana Birkby", .Phone = "394-555-0181"}, New Customer() With {.CustomerID = 2, .FullName = "Adriana Giorgi", .Phone = "117-555-0119"}, New Customer() With {.CustomerID = 3, .FullName = "Wei Yu", .Phone = "798-555-0118"} } End Sub Public Function GetCustomers() As List(Of Customer) Return _customers End Function Public Sub UpdateCustomer(SelectedCustomer As Customer) Dim customerToChange = _customers.Single(Function(c) c.CustomerID = SelectedCustomer.CustomerID) customerToChange = SelectedCustomer End Sub End Class End Namespace
A base class for view models is frequently added when implementing the MVVM pattern. The following example shows a base class.
Imports System.ComponentModel Namespace SimpleMVVM.ViewModel Public MustInherit Class ViewModelBase Implements INotifyPropertyChanged Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged Protected Overridable Sub OnPropertyChanged(propname As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propname)) End Sub End Class End Namespace
An implementation of the ICommand interface is frequently used with the MVVM pattern. The following example shows an implementation of the ICommand interface.
Imports System.Windows.Input Namespace SimpleMVVM.ViewModel Public Class RelayCommand Implements ICommand Private _isEnabled As Boolean Private ReadOnly _handler As Action Public Sub New(ByVal hander As Action) _handler = hander 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 End Namespace
The following example shows a simplified view model.
Imports System.Collections.Generic Imports SimpleMVVM.Model Namespace SimpleMVVM.ViewModel Public Class CustomerViewModel Inherits ViewModelBase Private _customers As List(Of Customer) Private _currentCustomer As Customer Private _repository As CustomerRepository Private _updateCustomerCommand As RelayCommand Public Sub New() _repository = New CustomerRepository() _customers = _repository.GetCustomers() WireCommands() End Sub Private Sub WireCommands() UpdateCustomerCommand = New RelayCommand(AddressOf UpdateCustomer) End Sub Public Property UpdateCustomerCommand() As RelayCommand Get Return _updateCustomerCommand End Get Private Set(value As RelayCommand) _updateCustomerCommand = value End Set End Property Public Property Customers() As List(Of Customer) Get Return _customers End Get Set(value As List(Of Customer)) _customers = value End Set End Property Public Property CurrentCustomer() As Customer Get Return _currentCustomer End Get Set(value As Customer) If _currentCustomer.Equals(value) Then _currentCustomer = value OnPropertyChanged("CurrentCustomer") UpdateCustomerCommand.IsEnabled = True End If End Set End Property Public Sub UpdateCustomer() _repository.UpdateCustomer(CurrentCustomer) End Sub End Class End Namespace
From a .NET Framework 4.5 app, Windows Store app, Silverlight-based app, or Windows Phone 7.5 app, you can reference the assembly that contains the model and view model projects. You then create a view that interacts with the view model. The following example shows a simplified Windows Presentation Foundation (WPF) app that retrieves and updates data from the view model. You could create similar views in Silverlight, Windows Phone, or Windows Store apps.
<Window x:Class="SimpleWPF.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:viewModels="clr-namespace:SimpleMVVM.ViewModel;assembly=SimpleMVVM.ViewModel" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <viewModels:MainPageViewModel x:Key="ViewModel" /> </Window.Resources> <Grid DataContext="{Binding Source={StaticResource ViewModel}}"> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="Auto"></RowDefinition> </Grid.RowDefinitions> <TextBlock Height="23" Margin="5" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="0" Name="textBlock2" Text="Select a Customer:" VerticalAlignment="Top" /> <ComboBox Height="23" HorizontalAlignment="Left" Grid.Column="1" Grid.Row="0" Name="CustomersComboBox" VerticalAlignment="Top" Width="173" DisplayMemberPath="FullName" SelectedItem="{Binding Path=CurrentCustomer, Mode=TwoWay}" ItemsSource="{Binding Path=Customers}" /> <TextBlock Height="23" Margin="5" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="1" Name="textBlock4" Text="Customer ID" /> <TextBlock Height="23" Margin="5" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="2" Name="textBlock5" Text="Name" /> <TextBlock Height="23" Margin="5" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="3" Name="textBlock9" Text="Phone" /> <TextBlock Height="23" HorizontalAlignment="Left" Grid.Column="1" Grid.Row="1" Name="CustomerIDTextBlock" Text="{Binding ElementName=CustomersComboBox, Path=SelectedItem.CustomerID}" /> <TextBox Height="23" HorizontalAlignment="Left" Grid.Column="1" Grid.Row="2" Width="219" Text="{Binding Path=CurrentCustomer.FullName, Mode=TwoWay}" /> <TextBox Height="23" HorizontalAlignment="Left" Grid.Column="1" Grid.Row="3" Width="219" Text="{Binding Path=CurrentCustomer.Phone, Mode=TwoWay}" /> <Button Command="{Binding UpdateCustomerCommand}" Content="Update" Height="23" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="4" Name="UpdateButton" VerticalAlignment="Top" Width="75" /> </Grid> </Window>