32
MVVM Light v5 v1.0 Ir Denis VOITURON http://www.dvoituron.be

MVVM Lights

Embed Size (px)

Citation preview

Page 1: MVVM Lights

MVVM Light v5

v1.0

Ir Denis VOITURON

http://www.dvoituron.be

Page 2: MVVM Lights

Pattern

MVVM Light - v1.0 2

ModelBusiness Logic

View

Presentation UI

ViewModel

Presentation Logic

Command

Binding

Method call

Event

MVVMMODEL VIEW VIEWMODEL

Page 3: MVVM Lights

Pattern

MVVM Light - v1.0 3

ModelBusiness Logic

View

Presentation UI

ViewModel

Presentation Logic

Command

Binding

Method call

Event

Friend

LastNameFirstNameDateOfBirthImageUrl

ObservableObject

FriendPage

<Page DataContext="{Binding Friend}"><TextBlock Text="{Binding FullName}" /><TextBlock Text="{Binding Age}" /><Image Source="{Binding ImageSource}" />

</Page>

FriendViewModel

FullNameAgeImageSource

ViewModelBase

NuGet : MVVM Light Libraries Only (PCL)

Page 4: MVVM Lights

MVVM Light v5

Toolkit to help MVVM developments◦ Windows Presentation Foundation (3.5, 4, 4.5, 4.5.1)

◦ Silverlight (4 and 5)

◦ Windows Phone (7.1, 8, 8.1 Silverlight, 8.1 RT)

◦ Windows Store (8, 8.1)

◦ Xamarin Android

◦ Xamarin iOS

◦ Xamarin Forms

Open Source project

Supported by Microsoft

MVVM Light - v1.0 4

Page 5: MVVM Lights

MVVM Light

Fundamentals

Page 6: MVVM Lights

Model - DataService

MVVM Light - v1.0 6

public interface IDataService{Task<Friend[]> GetFriendsAsync();

}

public class DataService : IDataService{

public async Task<Friend[]> GetFriendsAsync(){

...}

}

public class DesignDataService : IDataService{

public async Task<Friend[]> GetFriendsAsync(){

...}

}

Model

Page 7: MVVM Lights

Model – Data Classes

MVVM Light - v1.0 7

public class Friend{

public string FirstName { get; set; }public string LastName { get; set; }public DateTime DateOfBirth { get; set; }public string ImageUrl { get; set; }

}

public class Friend : ObservableObject{

private string _firstName = String.Empty;public string FirstName{

get { return _firstName; }set{

Set(() => this.FirstName, ref _firstName, value); }

}

Model

Page 8: MVVM Lights

IoC – Inversion of Control

MVVM Light - v1.0 8

public class MainViewModel{

public MainViewModel(){

_dataService = new DataService();}

}

public class MainViewModel{

public MainViewModel(IDataService dataservice){

_dataService = dataservice;}

}

DataService for Test, for Production, for Design, …

An IoC container is◦ Responsible to create services when needed.

◦ Responsible for injecting them.

◦ Responsible for caching the objects.

◦ And providing access to them.

View

Model

Page 9: MVVM Lights

ViewModelLocator

MVVM Light - v1.0 9

public class ViewModelLocator{

static ViewModelLocator(){

ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

SimpleIoc.Default.Register<IDataService, DataService>();

SimpleIoc.Default.Register<MainViewModel>();}

public MainViewModel Main{

get{

return ServiceLocator.Current.GetInstance<MainViewModel>();}

}}

View

Model

Page 10: MVVM Lights

ViewModelLocator

MVVM Light - v1.0 10

View

Model

public class MainViewModel : ViewModelBase{

public MainViewModel(Model.IDataService dataService){

}}

<Applicationx:Class="MyApplication.App"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:viewmodel="using:MyApplication.ViewModel"xmlns:local="using:MyApplication">

<Application.Resources><viewmodel:ViewModelLocator x:Key="Locator" />

</Application.Resources>

</Application>

<Page x:Class="MyApplication.View.MainPage"...DataContext="{Binding Source={StaticResource Locator}, Path=Main}">

Page 11: MVVM Lights

NotifyPropertyChanged

MVVM Light - v1.0 11

View

Model

public class Friend : ViewModelBase{

private string _firstName = String.Empty;public string FirstName{

get { return _firstName; }set{

Set(() => this.FirstName, ref _firstName, value); }

}

private bool _isWorking = false;public bool IsWorking{

get { return _isWorking; }set{

Set(ref _isWorking, value); }

}

Page 12: MVVM Lights

RelayCommand

MVVM Light - v1.0 12

private RelayCommand<FriendItem> _displayDetailCommand;

private void ExecuteDisplayDetailCommand(FriendItem item){Messenger.Default.Send<FriendItem>(item);_navigationService.NavigateTo(ViewModel.ViewModelLocator.FRIENDDETAIL_PAGEKEY, item.ID);

}

private void ExecuteDisplayDetailCommand(FriendItem item){

Messenger.Default.Send<FriendItem>(item);_navigationService.NavigateTo(ViewModel.ViewModelLocator.FRIENDDETAIL_PAGEKEY,

item.ID);}

private bool CanExecuteDisplayDetailCommand(FriendItem item){

return true;}

Page 13: MVVM Lights

Navigation & Dialog

MVVM Light - v1.0 13

View

Model

public class ViewModelLocator{

public const string DAYDETAIL_PAGEKEY = "DayDetailPage";

static ViewModelLocator(){

...

// NavigationServiceSimpleIoc.Default.Register<INavigationService>(() =>{

var navigationService = new Model.NavigationService();navigationService.Configure(DAYDETAIL_PAGEKEY, typeof(View.DayDetailPage));navigationService.Configure(SEARCH_PAGEKEY, typeof(View.SearchPage));navigationService.Configure(TIMEDETAIL_PAGEKEY, typeof(View.TimeDetailView));return navigationService;

});

// DialogServiceSimpleIoc.Default.Register<IDialogService>(() =>{

return new DialogService();});...

}}

navigation.NavigateTo(ViewModelLocator.DAYDETAIL_PAGEKEY, e.Date);

Page 14: MVVM Lights

View

ModelNavigation between ViewModels

In Source ViewModel

◦ Call NavigateTo method

In Target ViewModel

◦ Implement INavigableViewModel

◦ Override Page.OnNavigateTo

MVVM Light - v1.0 14

public interface INavigableViewModel{

void Activate(object parameter);void Deactivate(object parameter);

}

navigation.NavigateTo(ViewModelLocator.DAYDETAIL_PAGEKEY, e.Date);

protected override void OnNavigatedTo(NavigationEventArgs e){

base.OnNavigatedTo(e);

var navigableViewModel = this.DataContext as Model.INavigableViewModel;if (navigableViewModel != null)

navigableViewModel.Activate(e.Parameter);}

protected override void OnNavigatedFrom(NavigationEventArgs e){

base.OnNavigatedFrom(e);

var navigableViewModel = this.DataContext as Model.INavigableViewModel;if (navigableViewModel != null)

navigableViewModel.Deactivate(e.Parameter);}

Page 15: MVVM Lights

Dialog from ViewModel

To display a dialog message, use

DialogService registered in

ViewModelLocator

MVVM Light - v1.0 15

View

Model

await _dialogService.ShowMessage("Let's go to the friend detail.", "Title", "OK", () =>{

// This code will be executed after OK click.});

await _dialogService.ShowMessage("Continue?", "Title", "OK", "Cancel", (ok) => {

if (ok){

_navigationService.NavigateTo(ViewModel.ViewModelLocator.FRIENDDETAIL_PAGEKEY, _selectedItem.ID);

}});

Page 16: MVVM Lights

Async

CheckBeginInvokeOnUI

MVVM Light - v1.0 16

View

Model

public async Task LoadDataAsync(){

await Task.Run(async () =>{

var data = await _dataService.GetMyDataAsync();

DispatcherHelper.CheckBeginInvokeOnUI(() =>{

this.Items.AddRange(data);...

});});

}

public sealed partial class App : Application{

protected override void OnLaunched(LaunchActivatedEventArgs e){

DispatcherHelper.Initialize();...

InDesignMode, don’t user asynchronous tasks

Page 17: MVVM Lights

DataContext

From ViewModel Locator

◦ If constructor of ViewModel use only

IoC parameters.

MVVM Light - v1.0 17

View

<Page x:Class="MyApplication.View.MainPage"...DataContext="{Binding Path=Main, Source={StaticResource Locator}}">

public class MainViewModel : ViewModelBase{public MainViewModel(IDataService dataService, INavigationService navigationService){

...}

}

Page 18: MVVM Lights

DataContext

From Designer Creator

◦ If IoC Locator can not find the correct constructor

MVVM Light - v1.0 18

View

#if DEBUG/// <summary>/// This constructor is used in the Windows Phone app at design time,/// for the Blend visual designer./// </summary>public DetailViewModel() : this(SimpleIoc.Default.GetInstance<Model.IDataService>(),

SimpleIoc.Default.GetInstance<INavigationService>(), 0)

{...

}#endif

<Page x:Class="MyApplication.View.DetailPage"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"...xmlns:vm="using:MyApplication.ViewModel"d:DataContext="{d:DesignInstance Type=vm:DetailViewModel,

IsDesignTimeCreatable=True}">

public DetailViewModel(IDataService dataService, INavigationService navigationService, int id){

...

Page 19: MVVM Lights

MVVM Light

Design Mode

Page 20: MVVM Lights

Design Mode

Drawing UI with Visual Studio (Blend)

MVVM Light - v1.0 20

Page 21: MVVM Lights

DataService

DesignDataService

MVVM Light - v1.0 21

public class DesignDataService : IDataService{

private List<Friend> _friends = new List<Friend>();

public DesignDataService(){

_friends.Add(new Friend() { LastName = "Voituron", FirstName = "Denis", ... });_friends.Add(new Friend() { LastName = "Dubois", FirstName = "Anne", ... });

...}

}

Model

Page 22: MVVM Lights

Navigation

DesignNavigationService

MVVM Light - v1.0 22

public class DesignNavigationService : INavigationService{

public string CurrentPageKey{

get{

Debug.WriteLine("DesignNavigationService.CurrentPageKey");return String.Empty;

}}

public void GoBack(){

Debug.WriteLine("DesignNavigationService.GoBack()");}

public void NavigateTo(string pageKey, object parameter){

Debug.WriteLine(String.Format("DesignNavigationService.NavigateTo(\"{0}\", {1})", pageKey, parameter.ToString()));

}

public void NavigateTo(string pageKey){

Debug.WriteLine(String.Format("DesignNavigationService.NavigateTo(\"{0}\")", pageKey));}

}

Model

Page 23: MVVM Lights

Dialog

DesignDialogService

MVVM Light - v1.0 23

public class DesignDialogService : IDialogService{

public System.Threading.Tasks.Task ShowError(Exception error, string title, string buttonText, Action afterHideCallback)

{throw new NotImplementedException();

}

public System.Threading.Tasks.Task ShowError(string message, string title, string buttonText, Action afterHideCallback)

{throw new NotImplementedException();

}

...

}

Model

Page 24: MVVM Lights

ViewModelLocator

MVVM Light - v1.0 24

public class ViewModelLocator{

static ViewModelLocator(){

if (ViewModelBase.IsInDesignModeStatic){

DispatcherHelper.Initialize();

// DataService, Navigation and DialogSimpleIoc.Default.Register<IDataService>(() => new DesignDataService());SimpleIoc.Default.Register<INavigationService>(() => new DesignNavigationService());SimpleIoc.Default.Register<IDialogService>(() => new DesignDialogService());

// ViewModelsSimpleIoc.Default.Register<FriendViewModel>();

}else{

}}

}

View

Model

Page 25: MVVM Lights

MVVM Light

Best Practices

Page 26: MVVM Lights

Objectives

Define a manageable structure

Create Design data to draw UI easier

Create a DataService class to centralize all

external requests

Use always asynchronous operations

Display a working progress bar

MVVM Light - v1.0 26

Page 27: MVVM Lights

Universal project

Create Blank App (Universal App)

Add NuGet Packages for Solution

Select MVVM Light Libraries Only (PCL)◦ WPF4.5 (and 4.5.1)

◦ Windows Phone (Silverlight) 8

◦ Windows Phone (Silverlight) 8.1

◦ Windows Phone (RT) 8.1

◦ Windows Store (RT) 8 and 8.1

◦ Xamarin Android

◦ Xamarin iOS

◦ PCL (for class libraries)

MVVM Light - v1.0 27

Page 28: MVVM Lights

Universal Project

Shared

Design

DesignDataService

DesignDialogService

DesignNavigationService

Helpers

Extensions

VisualTreeHelper

Model

DataService

IDataService

INavigableViewModel

NavigationService

Friend

ViewModel

Friend

FriendViewModel

ViewModelLocator

Project Structure

MVVM Light - v1.0 28

WindowsPhone

Assets

Logo.png

Controls

SampleControl

View

FriendPage

Windows

Assets

Logo.png

Controls

SampleControl

View

FriendPage

Page 29: MVVM Lights

Asynchronous

IDataServices

DesignDataService

MVVM Light - v1.0 29

public interface IDataService{

Task<Friend[]> GetFriendsAsync();}

public class DesignDataService : Model.IDataService{

List<Model.Friend> _friends = new List<Model.Friend>();

public DesignDataService(){

_friends.Add(new Model.Friend() { FriendID = 1, LastName = "Voituron", ... _friends.Add(new Model.Friend() { FriendID = 2, LastName = "Dubois", ...

}

#pragma warning disable 1998 // Supress warning "This async method lacks 'await' op."

public async Task<Model.Friend[]> GetFriendsAsync(){

return _friends.ToArray();}

#pragma warning restore 1998}

Page 30: MVVM Lights

Asynchronous

ViewModel

MVVM Light - v1.0 30

public async void LoadDataAsync(){

this.IsWorking = true;

if (this.IsInDesignMode){

var data = await _dataService.GetFriendsAsync();this.Friends.AddRange(data);return;

}else{

await Task.Run(async () =>{

var data = await _dataService.GetFriendsAsync();DispatcherHelper.CheckBeginInvokeOnUI(() =>{

this.Friends.AddRange(data);});

});}

this.IsWorking = false;}

Page 31: MVVM Lights

MVVM Light

References