Sie sind auf Seite 1von 17

Hands-On Lab

Lab Manual for C#


Introduction to Model-View-ViewModel in WPF

Page i

Information in this document is subject to change without notice. The example companies, organizations, products, people, and events depicted herein are fictitious. No association with any real company, organization, product, person or event is intended or should be inferred. omplying with all applicable copyright laws is the responsibility of the user. !ithout limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means "electronic, mechanical, photocopying, recording, or otherwise#, or for any purpose, without the express written permission of $icrosoft orporation. $icrosoft may have patents, patent applications, trademar%ed, copyrights, or other intellectual property rights covering subject matter in this document. &xcept as expressly provided in any written license agreement from $icrosoft, the furnishing of this document does not give you any license to these patents, trademar%s, copyrights, or other intellectual property. ' ())* $icrosoft orporation. +ll rights reserved.

$icrosoft, $,-./,, $,, !indows, !indows NT, $,.N, +ctive .irectory, 0izTal%, ,12 ,erver, ,harePoint, /utloo%, PowerPoint, 3rontPage, 4isual 0asic, 4isual 55, 4isual 655, 4isual Inter.ev, 4isual ,ource,afe, 4isual 7, 4isual 67, and 4isual ,tudio are either registered trademar%s or trademar%s of $icrosoft orporation in the 8.,.+. and9or other countries. /ther product and company names herein may be the trademar%s of their respective owners.

Page ii

Contents
LAB 1: INTRODUCTION TO MODEL-VIEW-VIEWMODEL ..................................................................................IV 2ab /bjective....................................................................................................................................................... v &xercise : - 0ecome familiar with the application. ............................................................................................. vi &xercise ( ; reating a domain model. ............................................................................................................ vii Tas% : ; Implementing INotifyProperty hanged ........................................................................................... vii Tas% ( ; Implementing .ata 4alidation using I.ata&rrorInfo .........................................................................ix Tas% < - Implementing the 4iew$odel ........................................................................................................... xi Tas% = ; onnecting the 4iew$odel to the 4iew ...........................................................................................xv Tas% > ; ?unning the application .................................................................................................................. xvi

Page iii

Lab 1: Introduct on to Mode!-V e"-V e"Mode!


$odel-4iew-4iew$odel is a 8I pattern highly discussed and recommended amongst the !P3 development community. The main goal for implementing an $-4-4$ architecture is separation of concerns, which enables benefits li%e testability and good code maintenance. There are other patterns to separate concerns "e.g. $odel-4iew-Presenter# but 4iew-$odel is most often used in !P3 because it is very exploitive of !P3 features "li%e triggers, data-binding and commands#, which leads to new benefits li%e less code to maintain, and design-time support for creating the user experience using design tools li%e &xpression 0lend.

Page iv

Lab Ob#ect $e
&stimated time to complete this lab@ % &ours 'E(erc ses 1 and % ) 1:*+,- E(erc se * ) *+,. The objective of this lab is to create a very simple !P3 application that uses $-4-4$. In this lab, you will have a chance to complete the three following exercises@ reating a domain model that implements change notification. reating a domain model that implements validation. reating a basic user interface to showcase our application. reating a 4iew$odel to drive the user interface. reating commands to communicate between the 4iew$odel and 4iew.

This lab reAuires the following components which have all been installed on these machines@

Microsoft .Net Framework 3.5 Service Pack 1 Visual Studio 2008 Professional dition Service Pack 1 o S!" Server #om$act dition %comes wit& 3.5 SP1'.

Page v

E(erc se 1 - Beco,e /a, ! ar " t& t&e a00! cat on.


:. /pen the ,olution folder and 2aunch the $44$2ab.sln (. ?un the application " 3># or " trl 5 3>#

The application is a trivial ontact editor. It does some basic validation@ :. 3irst Name and 2ast Name must be a string, with characters "so length greater than )# (. 0irthday must be a valid date before todayBs date. <. Number of omputers is a number eAual to or greater than zero. !hen you enter invalid data, the application will tell you by showing an asteris% next to the field and by disabling the ,ave button "so you donBt attempt to save invalid data#.

Page vi

E(erc se % ) Creat n1 a do,a n ,ode!.


In this exercise you will create a class to maintain basic contact info information. The class will implement the following properties@ 2ro0ert3 T30e str n1 str n1 DateT ,e4 nt 2ro0ert3 Na,e 3irstName 2astName 0irthday Number/f omputers

Tas5 1 ) I,0!e,ent n1 INot /32ro0ert3C&an1ed :. Co to the start folder and open the $44$2ab.sln in 4isual ,tudio (. In the ,olution &xplorer Tab, Navigate to the $odel folder in the project

<. ?ight

lic% on $odel folder, and select +dd -D lass from the Popup menu

=. The +dd New Item dialog will come up, select the ode ategory and lass template and name the file 2ab ontact.cs.

Page vii

>. In the 2ab ontact.cs file, change the modifier of the class to public, and have your class implement the INotifyProperty hanged interface.
using System.ComponentModel; namespace MVVMLab { public class LabContact : INotifyPropertyC anged !

E. ?ight clic% on the INotifyProperty hanged and select Implement Interface from the popup menu. F. ?eview the code that 4isual ,tudio added for you@
"region INotifyPropertyC anged Members public e#ent PropertyC anged$#ent%andler PropertyC anged; "endregion

INotityProperty hanged will expose the Property hanged event that the !P3 data binding engine will listen for. !henever a property changes in the 2ab ontact class, it will raise this event. To raise the event, you implement a method called /nProperty hanged. *. reate a /nProperty hanged method that you will use to fire change notification. The method chec%s if any objects have subscribed to it, and calls it with a Property hanged&vent+rgs that includes the property name, passed in as a parameter.
protected #oid &nPropertyC anged'string name( { PropertyC anged$#ent%andler p ) PropertyC anged; if 'p *) null( p 't is+ ne, PropertyC anged$#ent-rgs'name((; !

Page viii

G. Now, add the 3irstName property to the class, it is of type string. 3or the .atabinding engine to find it, 3irstName needs to be a public property "not a public member#. In the setter for the 3irstName, we will chec% to ma%e sure the value is different.
public string .firstName; public string /irstName { get { return .firstName; ! set { if '.firstName *) #alue( { .firstName ) #alue; &nPropertyC anged'0/irstName0(; ! ! !

:). Now, we need to add all the other properties, repeat the step above for the following properties and types@ 2ro0ert3 T30e 2ro0ert3Na,e str n1 2astName DateT ,e4 0irthday nt Number/f omputers This 2ab ontact class now implements property change notification, that trivial interface and the method to fire is all it ta%es to have two-way data binding with the view. Tas5 % ) I,0!e,ent n1 Data Va! dat on us n1 IDataErrorIn/o I.ata&rrorInfo is the interface used for validation. There are other ways to implement validation in !P3 "e.g. using 4alidation?ules# but for domain objects I.ata&rrorInfo is standard, it allows you to %eep the validation within the domain model. :. +dd and implementation of I.ata&rrorInfo to your 2ab ontact class
public class LabContact : INotifyPropertyC anged + I1ata$rrorInfo {

(. ?ight clic% on the I.ata&rrorInfo and implement the interface. Notice the changes that 4isual ,tudio will ma%e for you.
"region I1ata$rrorInfo Members public string $rror { get { t ro, ne, NotImplemented$2ception'(; ! !

Page ix

public string t is3string columnName4 { get { t ro, ne, NotImplemented$2ception'(; ! ! "endregion

Hou need to override the indexer so that instead of throwing an exception, it validates the fields. !e will accomplish that in two steps@ <. reate a .ictionary property named &rrors, which will hold the error messages and expose them to our 4iew and 4iew$odel.
protected 1ictionary5string+ string6 .errors ) ne, 1ictionary5string+ string6'(; public I1ictionary5string+ string6 $rrors { get { return .errors; ! !

=. Now override the default accessor that I.ata&rrorInfo added. This method will be called by the .atabinding pipeline when a 8I control is changed. Its purpose is to determine if the $odel property has any validation errors.
public string t is3string columnName4 { get { string errorMessage ) string.$mpty; t is.$rrors.7emo#e'columnName(; s,itc 'columnName( { case 08irt day0: if 't is.8irt day.%asValue 99 t is.8irt day 6 { ! brea;; case 0Number&fComputers0: if 't is.Number&fComputers 5 <( { errorMessage ) 0Number of computers must be greater t an or e=ual to >ero.0; ! brea;; case 0/irstName0: if 'string.IsNull&r$mpty't is./irstName( ?? t is./irstName.:rim'(.Lengt )) < ( Page x errorMessage ) 08irt day must be before today.0;

1ate:ime.:oday(

{ !

errorMessage ) 0/irst name is a re=uired field.0;

brea;; case 0LastName0: if 'string.IsNull&r$mpty't is.LastName( ?? t is.LastName.:rim'(.Lengt )) <( { errorMessage ) 0Last name is a re=uired field.0; ! ! brea;;

if '*string.IsNull&r$mpty'errorMessage(( { t is.$rrors.-dd'columnName+ errorMessage(; ! ! ! return errorMessage;

/ur 2ab ontact class now implements basic "sample# validation and is ready for the 8ser Interface. Tas5 * - I,0!e,ent n1 t&e V e"Mode! The 4iew$odel will hold the state for the 4iew. In this simplistic example@ The 4iew will be a 8ser ontrol The 4iew$odel will be a simple class that implements change notification "INotifyProperty hanged#, and exposes three properties@ o New ommand ; used to create a new "and empty# instance of 2ab ontact o ,ave ommand ; used to save the 2ab ontact o 2ab ontact ; our domain model. 8I controls bind to this object. The 4iew$odel is the .ata ontext for the 4iew. The 4iew data binds its commands and 8I controls to properties on the 4iew$odel. 2etBs get to wor%I :. +dd a new class called 2ab ontact4iew$odel to the 4iew$odel folder

Page xi

!e need to implement change notification, here instead of implementing the interface li%e we did earlier, we will do a more framewor% oriented tas% and inherit from a class that implements change notification " and some basic validation#. The name of the class is 4iew$odel0ase. (. ?eview the 4iew$odel0ase class. Notice it implements INotifyProperty hanged and /nProperty hanged li%e you did earlier. <. .erive your 2ab ontact4iew$odel class from 4iew$odel0ase
public class LabContactVie,Model : Vie,Model8ase

=. 4isual ,tudio 7 environment does this feature where they add the namespace based on your directory, in this case to %eep things organized we added 2ab ontact4iew$odel to the 4iew$odel directory, so 4, might have created a namespace for you called $44$2ab.4iew$odel0ase, we need to change that since our lab uses $44$2ab Jsorry, we do this mostly to %eep things the same with 40Bs labK.
namespace MVVMLab

>. +dd a 2ab ontact property to your 2ab ontact4iew$odel. The type is 2ab ontact. J!ow is that sentence confusing or whatLK
pri#ate LabContact .labContact; public LabContact LabContact { get { return .labContact; ! set { if '.labContact *) #alue( { .labContact ) #alue; &nPropertyC anged'0LabContact0(; ! ! !

Page xii

E. +dd a read-only property called New ommand, of type I ommand to your 2ab ontact4iew$odel.
pri#ate ICommand .ne,Command; public ICommand Ne,Command { get { ! !

Notice that the command is part of the instance of 2ab ontact4iew$odel. It is not a static to ma%e commanding life-time manageable, you can do either one once you are more advanced. F. To implement the getter we will do two tas%s@ ma%e the actual command instance lazy-loaded "that means created only when getter is called#, cached per instance of 2ab ontact4iew$odel, and it will return a new instance of ?elay ommand. ?elay ommand is a generic, reusable implementation of I ommand. Hou might have also heard of it as the name .elegate ommand "from the omposite +pplication Cuidance#. ?elay ommand constructor has two overloads, one that ta%es the &xecute method to be called when I ommand.&xecute is called and another overload that ta%es the an&xecute delegate. In our application, New ommand can be called any time so we donBt implement an&xecute. 3eel free to browse through the implementation of ?elay ommand to get familiar with it. +lso note that we are passing delegates to the parameter and these methods are not yet created, we will do that further down the lab.
pri#ate ICommand .ne,Command; public ICommand Ne,Command { get { if '.ne,Command )) null( { .ne,Command ) ne, 7elayCommand'Ne,$2ecute(; ! return .ne,Command; ! !

*. Now add a property of type I ommand for the ,ave ommand, in this case the ?elay ommand will ta%e a &xecute and a an&xecute delegate.
pri#ate ICommand .sa#eCommand; public ICommand Sa#eCommand { get { if '.sa#eCommand )) null( { .sa#eCommand ) ne, 7elayCommand'Sa#e$2ecute+ CanSa#e$2ecute(; Page xiii

! return .sa#eCommand; ! !

Now we implement the methods for our commands. G. The New ommand method should create a new 2ab ontact and assign it to the 2ab ontact property in the 4iewmodel. ,ince our 8I is data-bound, this will refresh all the 8I.
pri#ate #oid Ne,$2ecute'ob@ect param( { AA:&1&: :ec nically you s ould sa#e old one+etc t is.LabContact ) ne, LabContact'(; !

:). ,ave ommand is also straight forward. !e are not going to implement the ,ave connectivity for real, but we will pop a debug message just so you %now it is wor%ing.
pri#ate #oid Sa#e$2ecute'ob@ect param( { AAIn a real applicaiton implement sa#ing t e LabContact to t e database AAIn MBVBVM applications t e Vie,Model does not raise CI Message 8o2es. AA Instead+ a Logger Ser#ice is used. Message8o2.S o,'0Sa#ed0(; !

::. +t last, an,ave ommand is interesting. This method will be called on-demand from the button to chec% if the ,ave ommand can be executed. an,ave ommand returns a 0oolean, if it is true, the command can &xecute, if false, command can not execute. 0utton and most other ommand,ources in !P3 are smart enough to MdisableN themselves when an&xecute is false. The basic instance of an,ave&xecute could just chec% if 2ab ontact has &rrors and if it does not have errors return true. Oowever, if we did that, we would miss out on 8I &xceptions during data-binding that are swallowed by the binding engine. 3or example, if you had a property of type .ateTime, and you entered the letter M+N in the Text0ox that uses .atabinding to assign it to a .ateTime, the data binding engine would never called I.ata&rrorInfo to validate this property since the type conversion failed. To catch these types of exceptions, we will listen for the 4alidation.&rror event on the 4iew, and the 4iew will set the 8I4alidation&rror ount in our 2ab ontact4iew$odel. ,o when implementing an&xecute, we are going to chec% for these two counts to be ).
pri#ate bool CanSa#e$2ecute'ob@ect param( { return base.CIValidation$rrorCount )) < 99 t is.LabContact.$rrors.Count )) <; ! Page xiv

/ur 4iew$odel is now ready. Tas5 6 ) Connect n1 t&e V e"Mode! to t&e V e" The lab project ships with 8I already created, but not data bound. ,o you could give the app a whirl now and run it, you will notice a few things@ The data is not bound, so changes are not reflected or validated. The commands are always enable. Hou can clic% on the buttons any time you want, even with invalid data. ,o, what do we need to doL .ata bind all the 8I to our 4iew$odelII :. /pen up 2ab ontact4iew.xaml in the 4iew folder. (. Hou should notice that there is a lot of Text0oxes in there with TextPNN Q They loo% li%e this@
5*BB /irstName BB6 5:e2t8o2 Drid.Column)0E0 Drid.7o,)0E0 :e2t)00 Fidt %ori>ontal-lignment)0Left0 Vertical-lignment)0:op0 5*BB LastName BB6 5:e2t8o2 Drid.Column)0E0 Drid.7o,)0I0 :e2t)00 Fidt %ori>ontal-lignment)0Left0 Vertical-lignment)0:op0 )0EG<0 Ma2Lengt )0H<0 A6 )0EG<0 Ma2Lengt )0H<0 A6

/n top of each Text0ox, to ma%e it easier we have put a comment with the field that the Text0ox should be data bound to. <. hange the data binding for the TextProperty so it binds to the 2ab ontact.RPropertyNameD above each textbox, li%e this@
5*BB /irstName BB6 5:e2t8o2 Drid.Column)0E0 Drid.7o,)0E0 :e2t)0{8inding LabContact./irstName!0 Fidt )0EG<0 Ma2Lengt )0H<0 %ori>ontal-lignment)0Left0 Vertical-lignment)0:op0 A6

JNote@ if you tried running the app now, you wonBt see the bindings wor%ing yet, do you %now whyLK =. That ta%es care of the binding, but if you run it now, validation is still not wor%ing, we need to add that to the binding too.
5:e2t8o2 Drid.Column)0E0 Drid.7o,)0E0 :e2t)0{8inding LabContact./irstName+ Notify&nValidation$rror):rue+ Validates&n1ata$rrors):rue+ Validates&n$2ceptions):rue!0 Fidt )0EG<0 Ma2Lengt )0H<0 %ori>ontal-lignment)0Left0 Vertical-lignment)0:op0 A6

>. !e could be deliciously evil and have you type that for all of the other properties, but we will not do that. ,croll to the bottom of the 2ab ontact4iew.xaml and you will find commented out text boxes with all the data bound syntax. 3eel free to copy that to the top and replace the current text boxes. There is a comment in the S+$2 that says Mbegin paste hereN and Mend paste hereN so you %now what to replace. E. +s you cut T paste, do notice that there are Text0oxes where we enter the text, and there is a corresponding Text0loc% displaying the value from 2ab ontact. In the lab, this is to show you

Page xv

what the Uactual valueB is in the 4iew$odel at any time. F. + few steps above, after we had wired a binding correctly you read that they are not wor%ing now. The reason for it is the .ata ontext has not been setI *. /pen up +pplication$ain!indow.xaml.cs and edit the constructor for +pplication$ain!indow so that a new instance of 2ab ontact4iew$odel is assigned to the !indowBs .ata ontext.
LabContactVie,Model labContactVie,Model ) ne, LabContactVie,Model '(; labContactVie,Model.LabContact ) ne, LabContact { 8irt day ) 1ate:ime.No,.-ddJears 'BI<(+ /irstName ) 0Ko n0+ LastName ) 0Smit 0+ Number&fComputers ) H!; t is.1ataConte2t ) labContactVie,Model;

If you loo% at +pplication$ain!indow.xaml you will notice it has a 2ab ontact4iew 8ser ontrol in it. !e are VnotV setting the .ata ontext for our view explicitly, we are actually assigning it to the !indowBs and since .ata ontext is an inherited property, our 2ab ontact4iew inherits it. Tas5 7 ) Runn n1 t&e a00! cat on +fter all that wor% we are finally ready to run the app. .rum-roll please. :. ?un the application. The 8I should come up, and if you initialized the data li%e we did above, you should see it.

(. .elete all the characters from the 3irst Name Text0ox and press the T+0 %ey. Notice it is validated and shows an error. Notice that the ,ave command is disabled "because an,ave&xecute returned false#.

Page xvi

<. .elete all the characters from the 0irthday field and press the T+0 %ey. Notice that the Nullable .ate property 0irthday does not display an error and sets the property value to null. This causes 0irthday Text0loc% field to clear. =. Now enter a M:(9(>9())*N in the 0irthday Text0ox and press the T+0 %ey. &verything is normal. >. Now enter M0N in the 0irthday Text0ox and press the T+0 %ey. Notice the error indicator and also notice that the 4iew$odel still holds the :(9:(>9())* value.

Page xvii

Das könnte Ihnen auch gefallen