Le Model View ViewModel (MVVM) est une architecture et une méthode de conception utilisée dans le génie logiciel. Apparu en 2004, MVVM est adapté pour le développement des applications basées sur les technologies Windows (desktop, tablette ou mobile), mais également Web/HTML5. Cette méthode permet de séparer la vue de la logique et de l'accès aux données en accentuant les principes de binding et d’événement. Une bonne vue générale a été donnée par Laurent Bugnion, le père du MVVMLight, dans le podcast #11 de DevApps (http://devapps.be/podcast/11).
http://www.meetup.com/fr-FR/micbelgique/events/228368909/?eventId=228368909
8. 8
• 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
9. 9
MVVM Light v5
Toolkit to help MVVM developments
Open Source project
Supported by Microsoft
http://www.mvvmlight.net
13. 13
public 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
14. 14
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
15. 15
public class MainViewModel
{
public MainViewModel()
{
_dataService = new DataService();
}
}
public class MainViewModel
{
public MainViewModel(IDataService dataservice)
{
_dataService = dataservice;
}
}
DataService for Test, for Production, for Design, …
16. 16
public 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>();
}
}
View
Model
<Application xmlns:locator="using:SampleMvvmLight.ViewModels" ... >
<Application.Resources>
<locator:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>
</Application>
17. 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;
}
}
View
Model
18. 18
Snippet ‘mvvminpcsetlambda’
View
Model
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);
}
}
}
19. 19
private ViewModels.MainViewModel ViewModel
{
get { return ((MainViewModel)Resources["ViewModel"]); }
}
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>
20. 20
Rules for views and view models
User controls vs templated controls
@ricosuter
23. 23
Interface 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();
}
25. 25
private 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()
{
...
}
}
26. 26
Navigate 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);
}
}
27. 27
Resource files
• Resw
Need a T4 file
• Resx
Configurable
MyResources.Culture = new CultureInfo("fr");
CultureInfo.CurrentCulture = new CultureInfo("fr");
28. 28
The following prefixes or postfixes are recommended:
(bigger projects, the keys should start with a module name. e.g. ‘Search_ButtonOK’)
*
*
*
*
*
*
*
*
*
* @ricosuter
36. 36
• 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);
}