Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

39
1 Denis Voituron .Net Software Architect MVVM Présentation et bonnes pratiques [email protected] www.dvoituron.be @denisvoituron

Transcript of Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

Page 1: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

1

Denis Voituron.Net Software Architect

MVVMPrésentation et bonnes pratiques

[email protected] www.dvoituron.be @denisvoituron

Page 2: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

2

About meDenis Voituron

Civil engineer (Mons)Company founderDeveloper: VB3, VB.Net, C#.Net Software Architect (Trasys)BloggerSpeaker (DevApps.be)

www.dvoituron.be

Page 3: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

3

Agenda• Introduction• Getting Started

• Model• View• ViewModelBase

• Services and DesignServices• Localization• Commands• Messenger• Unit Tests and TestServices

Page 4: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

4

Introduction

Page 5: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

5

Model

Pattern

ViewModelViewBinding

Methods callMethods call

Events

Database

Page 6: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

6

Pattern

ModelBusiness Logic

ViewPresentation UI

ViewModel

Presentation Logic

CommandBinding

Method call

Event

MVVMMODEL VIEW VIEWMODEL

Page 7: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

7

Pattern

ModelBusiness Logic

ViewPresentation UI

ViewModel

Presentation Logic

CommandBinding

Method call

Event

Friend

FirstNameLastNameDateOfBirthPictureUrl

ObservableObject

FriendPage<Page DataContext="{Binding ViewModel}"> <TextBlock Text="{Binding FullName}" /> <TextBlock Text="{Binding Age}" /> <Image Source="{Binding Photo}" /></Page>

FriendViewModel

FullNameAgePhoto

ViewModelBase

Page 8: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

8

Why?• Code behind is not always bad,

but can complicate things.• MVVM is a variation of MVC.• The goal is to decouple

the View from the Model.• Easier to maintain.• Easier to test• Allow to create design time data.

@LBugnion

Page 9: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

9

MVVM Light v5Toolkit 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)Universal App Plateform (UAP – Windows 10)Xamarin AndroidXamarin iOSXamarin Forms

Open Source project

Supported by Microsoft

http://www.mvvmlight.net

Page 10: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

10

Page 11: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

11

Getting Started

Page 12: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

12

Project File StructureImages and icons.Reusable UI controls, without view models.

Classes and resources.for application localization.

Model and domain classes.(For bigger project, move it to a separate project)

View models classes.(For bigger project, move it to a separate project)

Contains the views.Including a ‘Dialogs’ folder if necessary.With suffix (Window, Dialog, Page or View).Entry point.

Page 13: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

13

Modelpublic class Friend{ public int ID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Picture { get; set; } public string Location { get; set; } public string Message { 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 14: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

14

Modelpublic 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 15: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

15

IoC – Inversion of Controlpublic 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.

Page 16: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

16

ViewModelLocatorpublic class ViewModelLocator{ public ViewModelLocator() { // SimpleIoC ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

// Models if (ViewModelBase.IsInDesignModeStatic) SimpleIoc.Default.Register<IDataService, DataService>(); else SimpleIoc.Default.Register<IDataService, DesignDataService>();

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

ViewMode

l

<Application xmlns:locator="using:SampleMvvmLight.ViewModels" ... > <Application.Resources> <locator:ViewModelLocator x:Key="ViewModelLocator" /> </Application.Resources></Application>

Page 17: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

17

public abstract class ViewModelBase : GalaSoft.MvvmLight.ViewModelBase{ // Default constructor used by the Design or Production Mode public ViewModelBase() : this(ServiceLocator.Current.GetInstance<Models.IDataService>(), ServiceLocator.Current.GetInstance<IDialogService>(), ServiceLocator.Current.GetInstance<INavigationService>()) { if (ViewModelBase.IsInDesignModeStatic) OnLoadedAsync(); }

// Default constructor with all usable services protected ViewModelBase(IDataService dataservice, IDialogService dialogService, INavigationService navigationService) { this.DateService = dataservice; this.DialogService = dialogService; this.NavigationService = navigationService; }}

ViewModelBase ViewMode

l

Page 18: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

18

NotifyPropertyChanged

Snippet ‘mvvminpcsetlambda’

ViewMode

l

public class MainViewModel : ViewModelBase{ protected async override Task OnLoadedAsync() { this.Friends = await this.DateService.GetFriendsAsync(); }

private Friend[] _friends = null; public Friend[] Friends { get { return _friends; } set { Set(() => Friends, ref _friends, value); } }}

Page 19: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

19

private ViewModels.MainViewModel ViewModel{ get { return ((MainViewModel)Resources["ViewModel"]); }}

View View

<Page xmlns:vm="using:SampleMvvmLight.ViewModels">

<!-- Create a new instance of the associated ViewModel --> <Page.Resources> <vm:MainViewModel x:Key="ViewModel" /> </Page.Resources>

<!-- Content --> <Grid DataContext="{StaticResource ViewModel}"> <ListView Margin="10,33,10,10" ItemsSource="{Binding Friends}" /> </Grid>

</Page>

Page 20: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

20

Best practicesRules for views and view models

• Separate the user interface from the application logic.• Always a one-to-one relationship between the view and

view model.• Not allowed to directly access the view from the view

model.• If a view becomes too large, split it up into multiple views.• Use a ViewModelBase class to simplify implementations.

User controls vs templated controls

• User control when implementing an application specific view

• Templated control when implementing a reusable UI control

@ricosuter

Page 21: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

21

Best practicesView model instantiation

• View model should be instantiated in XAML.

• NOT set the DataContext in the XAML code where the view (User Control) is instantiated

• To pass parameters to a view, always use dependency properties

• Handling the view model life cycle

<MySubView DataContext="{Binding MySubViewModel}" />

<MySubView Project="{Binding SelectedProject}" />

public MyView(){ InitializeComponent(); Loaded += async delegate { await ViewModel.OnLoaded(); }; Unloaded += async delegate { await ViewModel.OnUnloaded(); };}

Page 22: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

22

Services

Page 23: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

23

Data ServiceInterface for testing, designing and running

Sample

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

public class DataService : IDataService{ const string UrlBase = "http://xxx.azurewebsites.net/friends.aspx";

public async Task<Friend[]> GetFriendsAsync() { var client = new HttpClient(); string json = await client.GetStringAsync(new Uri(UrlBase));

var result = JsonConvert.DeserializeObject<ListOfFriends>(json); return result.Data.ToArray(); }

Page 24: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

24

Dialog Service

SimpleIoc.Default.Register<IDialogService, DialogService>();

await this.DialogService.ShowMessage ("My message", "My title");

await this.DialogService.ShowMessageBox("My message", "My title");

await this.DialogService.ShowError(MyException, ...);

Page 25: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

25

Navigation Serviceprivate INavigationService CreateNavigationService(){ var navigationService = new NavigationService(); navigationService.Configure(MAIN_PAGE, typeof(MainPage)); navigationService.Configure(DETAIL_PAGE, typeof(DetailPage)); return navigationService;}

...

SimpleIoc.Default.Register<INavigationService>(() => CreateNavigationService());

public class NavigationService : INavigationService{ public void NavigateTo<T>(string pageKey, T parameter) { ... }

public void GoBack() { ... }}

Page 26: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

26

Navigation ServiceNavigate to the second page

Retrieve parameters

public class FirstViewModel : ViewModelBase{ ... this.NavigationService.NavigateTo<string>(ViewModelLocator.DETAIL_PAGE, "ABC");}

public class SecondViewModel : ViewModelBase{ public DetailViewModel() { this.NavigationRegistering<int>(); }

protected async override Task OnNavigationFrom(object parameter) { this.Friend = await this.DateService.GetFriendAsync((int)parameter); }}

Page 27: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

27

LocalizationResource files• Resw

Need a T4 file

• ResxConfigurable

MyResources.Culture = new CultureInfo("fr");

CultureInfo.CurrentCulture = new CultureInfo("fr");

Page 28: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

28

LocalizationThe following prefixes or postfixes are recommended:(bigger projects, the keys should start with a module name. e.g. ‘Search_ButtonOK’)

• Label*: Labels which belong to an input control• Button*: Button content texts• Message*Title: Titles for message boxes• Message*Content: Content messages for message boxes• Log*: Log messages• Header*: Header texts to use in window titles, tab

headers and similar locations

• GridHeader*: Data grid headers• Menu*: Strings to use in menu items (e.g. context

menu)• RibbonHeader*: Labels used in the ribbon tab• RibbonButton*: Headers for ribbon buttons

@ricosuter

Page 29: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

29

Commands

Page 30: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

30

Relay CommandCommands are instantiated in constructor.

• Name "xxxCommand"• "xxxExecute" and "CanXxxExecute"

public MainViewModel(){ DisplayDetailCommand = new RelayCommand<Friend> (DisplayDetailExecute, CanDisplayDetailExecute);}

public RelayCommand<Friend> DisplayDetailCommand { get; private set; }

public void DisplayDetailExecute(Friend parameter){ ...}

public bool CanDisplayDetailExecute(Friend parameter){ return true;}

<Button Command="{Binding DisplayDetailCommand}" CommandParameter="{Binding SelectedFriend}" />

Snippet ‘mvvmrelaymethodcanexecute’

Page 31: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

31

Messenger

Page 32: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

32

What does the Messenger do?• It is an "event bus".

• A message distribution system.• One object broadcasts a message.• Other objects register to receive these messages.• The sender doesn’t know who receives the messages.• The receiver doesn’t know who sent the messages.

• One default instance (Messenger.Default)

@LBugnion

Page 33: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

33

MessengerMessenger.Default.Send<string>("Hello World");

Messenger.Default.Register<string> ( this, (message) => { ... } );

Messenger.Default.Unregister<string>(this);

Page 34: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

34

Unit Tests

Page 35: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

35

Test Helpers• TestDataService• TestDialogService• TestNavigationService

• TestServiceRegisterpublic static void Registering(){ ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

SimpleIoc.Default.Register<IDataService, TestDataService>(); SimpleIoc.Default.Register<IDialogService, TestDialogService>(); SimpleIoc.Default.Register<INavigationService, TestNavigationService>();}

Page 36: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

36

Unit Test• Initialization

• Test

[TestClass]public class MainViewModelTests{ [TestInitialize] public void Initialize() { TestServiceRegister.Registering(); }}

[TestMethod]public async Task ComputeNumberOfFriends(){ MainViewModel main = new MainViewModel(); await main.CallOnLoaded();

Assert.AreEqual(3, main.Friends.Length);}

Page 37: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

37

Conclusion

Page 38: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

38

Conclusion• MVVM Pattern• Project File Structure• Rules for views and view models• View model instantiation

ViewModelBase

• Services and DesignServicesDataServiceDialogServiceNavigationService

• Localization• Commands• Messenger• Unit Tests and TestServices

Page 39: Présentation et bonnes pratiques du pattern MVVM - MIC Belgique

39

Ressources• http://www.mvvmlight.net

• http://mvvmlight.codeplex.com

• https://www.pluralsight.com/courses/mvvm-light-toolkit-fundamentals

• http://blog.rsuter.com/recommendations-best-practices-implementing-mvvm-xaml-net-applications

• https://blog.rsuter.com/winrt-access-string-resources-in-a-stongly-typed-way-c