La prima vedere, se pare că ideea de a aplica un șablon MVVM pentru a elimina majoritatea conținutului unui fișier de cod separat este adecvată numai pentru elementele care generează valori. Dacă vine vorba de butoane, conceptul începe să se rătăcească. Obiectul Buton inițiază un eveniment Click. Acest eveniment trebuie procesat într-un fișier de cod separat. Dacă modelul de vizualizare efectiv implementează logica butonului (care este probabil), manipulatorul evenimentului trebuie să apeleze metoda modelului de vizualizare. O astfel de soluție este permisă din punct de vedere arhitectural, dar se dovedește a fi încă greoaie.
Din fericire, evenimentul Click are o alternativă, ideală pentru MVVM. Uneori se numește informal "interfața de comandă". Clasa ButtonBase definește proprietățile cu numele Comandă (tip ICommand) și CommandParameter (tip de obiect), care permite butonului să apeleze modelul vizualului prin legarea datelor.
Proprietățile Command and CommandParameter sunt suportate de proprietățile de dependență; aceasta înseamnă că pot fi receptoare de legături. Proprietatea Comandă este aproape întotdeauna un receptor de legare a datelor, proprietatea CommandParameter este opțională. De exemplu, poate fi folosit pentru a distinge butoanele legate de un singur obiect Command și de obicei are aceleași funcții ca și proprietatea Tag.
Să presupunem că ați scris o aplicație pentru calculator al cărei nucleu este implementat sub forma unui model de reprezentare specificat de proprietatea DataContext. O instanță a butonului de calculator pentru executarea comenzii de adăugare (+) poate fi creată în marcajul XAML:
Aceasta înseamnă că modelul de vizualizare conține proprietatea CalculateCommand a tipului ICommand. care este probabil definit după cum urmează:
Modelul de vizualizare trebuie să inițializeze proprietatea CalculateComnand, oferindu-i o instanță a clasei care implementează interfața ICommand, care este definită după cum urmează:
Când se face clic pe acest buton, metoda Execute a obiectului la care se referă CalculateCommand se numește cu argumentul "add". Se pare că Button gestionează apelul direct la modelul de vizualizare (sau mai degrabă la clasa care conține metoda Execute).
Ceilalți doi membri ai interfeței ICommand se referă la verificarea posibilității de a executa o comandă specifică la un anumit moment. Dacă comanda este în prezent nevalidă (de exemplu, calculatorul nu poate efectua adăugarea din cauza absenței numărului introdus), butonul corespunzător trebuie blocat.
Iată cum funcționează acest mecanism: atunci când analizăm și încărcăm XAML la momentul executării, proprietatea Comandă a obiectului Buton este setată la o legătură (în acest exemplu) cu obiectul CalculateCommand. Butonul atribuie un handler pentru evenimentul CanExecuteChanged și apelează metoda CanExecute a obiectului cu argumentul (în acest exemplu) "add". Dacă CanExecute returnează false, butonul Buton se blochează. În viitor, Button cheamă în mod repetat CanExecute când evenimentul CanExecuteChanged este declanșat.
Pentru a include o comandă în modelul dvs. de vizualizare, trebuie să furnizați o clasă care implementează interfața ICommand. Cu toate acestea, o astfel de clasă va avea cel mai probabil nevoie să acceseze proprietățile clasei modelului de reprezentare și invers.
Se pune întrebarea: pot fi combinate aceste clase?
Teoretic este posibil, dar numai dacă aceleași metode Execute și CanExecute pot fi utilizate pentru toate butoanele de pe pagină; pentru aceasta, fiecare buton trebuie să aibă o valoare unică CommandParameter, prin care poate fi identificat. Dar mai întâi aș vrea să demonstrez o modalitate standard de a implementa comenzi într-un model de prezentare.
Class DelegateCommand
Rescriem aplicația SimpleKeypad. creat mai devreme, astfel încât să utilizeze un model de prezentare pentru a acumula intrări de taste și pentru a genera un șir formatat. Pe lângă implementarea interfeței INotifyPropertyChanged, modelul de vizualizare se ocupă și de comenzile de la toate butoanele de pe tastatură. Operatorii de clicuri nu mai sunt necesari.
Și acum problema: că modelul de vizualizare manipulează comenzile butonului, trebuie să aibă una sau mai multe proprietăți ale tipului ICommand; acest lucru înseamnă că avem nevoie de una sau mai multe clase care implementează interfața ICommand. Pentru a implementa ICommand, aceste clase trebuie să conțină metodele Execute și CanExecute, precum și evenimentul CanExecuteChanged. În acest caz, corpurile acestor metode trebuie să interacționeze cu siguranță cu alte părți ale modelului de reprezentare.
Problema este rezolvată prin definirea metodelor Execute și CanExecute din clasa modelului de prezentare sub diferite denumiri unice. Apoi este definită o clasă specială care implementează ICommand și solicită direct metodele modelului de prezentare.
Această clasă specială este adesea numită DelegateCommand. După ce te-ai uitat în jur, vei găsi mai multe implementări diferite ale acestei clase, inclusiv o implementare din infrastructura Microsoft Prism care ajută dezvoltatorii să implementeze modelul MVVM în WPF (Windows Presentation Foundation) și Silverlight. Mai jos este implementarea mea.
Clasa mea DelegateCommand implementează interfața ICommand; aceasta înseamnă că conține metodele Execute și CanExecute, precum și evenimentul CanExecuteChanged. Dar, după cum se dovedește, DelegateCommand are nevoie de o altă metodă pentru a iniția evenimentul CanExecuteChanged. O numim RaiseCanExecuteChanged. Mai întâi, trebuie să definiți interfața care implementează ICommand și să conțină această metodă suplimentară:
Clasa DelegateCommand implementează interfața IDelegateCommand și utilizează delegații generici simpli (dar utile) definite în spațiul de nume al sistemului. Acești delegați predefiniți sunt numiți Acțiune și Func și primesc 1 până la 16 argumente. Funcții delegați returnează un obiect de un anumit tip, iar delegații de acțiune nu o fac. Delegat de acțiune
Clasa implementează metodele Execute și CanExecute, însă aceste implementări numesc pur și simplu metodele stocate în câmpuri. Câmpurile sunt completate de constructorul de clase cu argumentele transmise.
De exemplu, dacă există o comandă de calcul în modelul de vizualizare, proprietatea CalculateCommand poate fi definită în ea:
Modelul de vizualizare definește, de asemenea, două metode cu numele ExecuteCalculate () și CanExecuteCalculate ():
Modelul constructor de clasă de vizualizare creează proprietatea CalculateCommand creând o instanță a DelegateCommand cu aceste două metode:
Acum că ați înțeles ideea generală, luați în considerare modelul de aspect al tastaturii. Pentru textul de intrare și afișare, modelul de vizualizare definește două proprietăți: InputString și versiunea formatată a DisplayText.
Modelul de vizualizare definește de asemenea două proprietăți ale tipului IDelegateCommand cu numele AddCharacterCommand (pentru cheile numerice și de caractere) și DeleteCharacterCommand. Aceste proprietăți sunt create prin crearea unei instanțe a DelegatuluiCommand cu metodele ExecuteAddCharacter (), ExecuteDeleteCharacter () și CanExecuteDeleteCharacter (). Metoda CanExecuteAddCharacter () nu există deoarece scrierea este întotdeauna validă.
Metoda ExecuteAddCharacter () presupune că un caracter introdus de utilizator este trecut în parametru. Deci, o comandă este partajată pentru mai multe butoane.
Metoda CanExecuteDeleteCharacter () returnează adevărat numai dacă există caractere care pot fi șterse; în caz contrar, butonul de ștergere trebuie dezactivat. Dar această metodă este chemată numai atunci când legarea este inițializată, și numai atunci când evenimentul CanExecuteChanged este declanșat. Logica pentru declanșarea acestui eveniment este în metoda InputString (), care compară valorile returnate ale CanExecuteDeleteCharacter înainte și după schimbarea șirului de intrare.
Fișierul XAML creează o instanță a modelului de vizualizare ca resursă și apoi definește DataContext în rețea. Rețineți simplitatea legăturilor de comandă pentru treisprezece comenzi Button și utilizarea CommandParameter pentru cheile numerice și de caractere:
Partea cea mai neinteresantă a acestui proiect este fișierul de cod separat, care nu conține decât un apel InitializeComponent. Sarcina este finalizată.
Puteți utiliza caracteristicile Windows Runtime pentru a lansa o aplicație mobilă pentru un magazin online. Anterior, când am avut în vedere dezvoltarea magazinului pe ASP.NET MVC, nu am descris detalii importante despre deschiderea unui magazin on-line prin formalizarea oficială a documentelor, crearea serviciilor de asistență, livrarea, organizarea lucrărilor de curierat etc.