WPF -, -
Ако смятате, че изграждането на WCF услуга е трудно (както мислех), тогава ще оцените, че изграждането на Windows Presentation Foundation (WPF) приложение е почти толкова лесно, колкото изграждането на конзолно приложение.
Входната точка към WPF приложение е доста очевидна и не е сложна и въпреки че не предоставяме шевове, които са изрично проектирани да позволяват DI, можем лесно да оформим приложението по какъвто начин предпочитаме.
Състав на WPF
Входната точка към WPF приложение е дефинирана в неговия клас App. Подобно на повечето други WPF класове, този клас е разделен на два файла: App.xaml и App.xaml.cs. Можем да определим какво се случва по време на етапа на стартиране и в двата файла в зависимост от нашите нужди.
Когато създавате нов WPF проект във Visual Studio, файлът App.xaml дефинира атрибут StartupUri, който задава кой прозорец да се показва при стартиране на приложението - в този пример Window1:
По този начин методът OnStartup става Composition Root на приложението. Можете да използвате контейнер DI или Poor Man's DI, за да създадете прозорец. Следващият пример използва DI на Poor Man, за да илюстрира, че не е нужно да разчитате на възможностите на който и да е конкретен DI контейнер.
Пример: Управление на инвентара Ценно прикачване на клиента
В предишния пример разработихме уеб услуга, която можем да използваме за управление на продуктов каталог в приложение за шаблон Commerce. В този пример ще създадем WPF приложение, което използва тази уеб услуга за управление на продукти. Фигура 7-10 показва екранна снимка на това приложение.
Приложението прилага подхода Model View ViewModel (MVVM) и съдържа три нива, коитопоказано на фигура 7-11. Обикновено държим частта, която съдържа по-голямата част от логиката, изолирана от други модули - в този пример PresentationLogic.ProductManagementClient е скромен изпълним файл, който върши повече работа, отколкото просто дефиниране на потребителския интерфейс и делегиране на изпълнение на други модули.
С подхода MVVM предаваме ViewModel на свойството DataContext на главния прозорец, а машината за обвързване на данни и машината за шаблони на данни се грижат за правилното представяне на данните веднага щом вплетем нови ViewModels или променим данните на съществуващите ViewModels.
MVVM
Изглед на модел ViewModel (MVVM) е модел на проектиране, за който WPF е страхотен. Той разделя кода на потребителския интерфейс на три отделни отговорности.
Моделът е основният модел на приложение. Това често, но не винаги, е моделът на домейна. Често се състои от POCO. В настоящия пример моделът на домейн е внедрен в уеб услуга, така че нямате истински модел на домейн на това ниво. Въпреки това, приложението функционира с абстракции, разположени върху проксито на уеб услугата, и това е вашият модел. Обърнете внимание, че моделът обикновено се изразява по неутрален от потребителския интерфейс начин. Това не означава, че моделът ще бъде директно изложен от потребителския интерфейс, така че не излага никаква специфична за WPF функционалност.
View е потребителският интерфейс, който обмисляме. В WPF можем официално да изразим View в XAML и да използваме машината за свързване на данни и машината за шаблони на данни, за да представим данните. Възможно е да се изразят изгледи без използване на задния код.
ViewModel е мост между View и Model.Всеки ViewModel е клас, който трансформира и излага модела по специфичен, специфичен начин. В WPF това означава, че ViewModel може да разширява списъци като ObservableCollections и други подобни.
Инжектиране на зависимости в основния ViewModel
MainWindow съдържа само XAML маркиране и не съдържа дефиниран от потребителя код. Вместо това той използва механизъм за обвързване на данни, за да показва данни на екрана и да управлява потребителски команди. За да активираме това, трябва да предадем MainWindowViewModel към неговото свойство DataContext.
MainWindowViewModel излага данни като списък с продукти, както и команди за създаване, актуализиране или изтриване на продукт. Дали тази функционалност е възможна зависи от услугата, която предоставя достъп до продуктовия каталог: абстракцията IProductManagementAgent.
В допълнение към IProductManagementAgent, MainWindowViewModel също се нуждае от услуга, която може да използва, за да контролира своята среда за прозорци, като например показване на модални диалогови прозорци. Тази зависимост се нарича IWindow.
MainWindowViewModel използва шаблона за инжектиране на конструктор със следния подпис на конструктора:
За да свържем приложението, трябва да създадем MainWindowViewModel и да го предадем на свойството DataContext на екземпляра MainWindow.
Свързване на MainWindow и MainWindowViewModel
Това, което подправя този пример, е фактът, че за да внедрите правилно IWindow, имате нужда от указател към действителен WPF прозорец (MainWindow); но ViewModel изисква IWindow, а свойството DataContext на екземпляра MainWindow трябва да бъде ViewModel. С други думи, получавате кръгова зависимост.
В глава 6 се занимавахме с кръгови зависимости ипреминах през съответната част от този конкретен пример, така че няма да го повтарям в тази глава. Достатъчно е да се каже, че е въведена MainWindowViewModelFactory, която е отговорна за създаването на екземпляри на MainWindowViewModel.
Вие използвате тази фабрика в изпълнение на IWindow, наречено MainWindowAdapter, за да създадете MainWindowViewModel и да го предадете на свойството DataContext на екземпляра на MainWindow:
Членската променлива vmFactory е екземпляр на IMainWindowViewModelFactory и вие предавате на неговия метод Create екземпляр на съдържащия се клас, който имплементира IWindow. Полученият екземпляр на ViewModel след това се предава на DataContext на WpfWindow, който е екземпляр на MainWindow.
Забележка
Няма да навлизам в подробности, тъй като ги разгледахме в Глава 6. Върнете се към тази глава и прочетете раздела за кръгови зависимости, ако трябва да освежите какво се случва в момента.
Подсказка
Механизмът за обвързване на данни на WPF изисква да предадем зависимост ( ViewModel ) към свойството DataContext. По мое мнение това е погрешно използване на Property Injection, тъй като сигнализира, че зависимостта не е задължителна, което абсолютно не е така. Въпреки това, WPF 4 въвежда нещо, наречено XamlSchemaContext, което може да се използва като шев, което от своя страна ни дава повече гъвкавост, когато става въпрос за инстанциране на изгледи въз основа на маркиране.
Фигура 7-12 показва крайната диаграма на зависимостта на приложението.
Сега, след като сте идентифицирали всички градивни елементи на приложението, можете да ги сглобите. За да запазите DI кода на бедния човек симетричен, използвайтетози DI контейнер, аз го внедрих като метод Resolve в потребителски клас контейнер. Следващият списък показва изпълнението.
Това, което в крайна сметка връщате, е екземпляр на IWindow, реализиран от MainWindowAdapter, и за това имате нужда от WPF Window и IMainWindowViewModelFactory. Първият прозорец, който искате да покажете на потребителите, трябва да бъде MainWindow, така че това е, което предавате на MainWindowAdapter.
MainWindowViewModelFactory използва шаблона за инжектиране на конструктор, за да поиска IProductManagementAgent, така че трябва да обедините WcfProductManagementAgent с две от неговите зависимости.
Последният MainWindowAdapter, върнат от метода, обгръща MainWindow, така че когато извикаме метода Show, той делегира на метода Show на MainWindow. Точно това ще правите в Composition Root.
Внедряване на корен на композиция
След като вече знаете как да свържете приложението, всичко, което трябва да направите, е да го направите на правилното място. Както е описано в предишния раздел, първо трябва да отворите App.xaml и да премахнете атрибута StartupUri, защото искате сами изрично да оформите първоначалния прозорец за стартиране.
След като направите това, всичко, което трябва да направите, е да замените метода OnStartup в App.xaml.cs и да извикате контейнера.
В този пример използвате персонализиран ProductManagementClientContainer, но можете също да използвате общ DI контейнер като Unity или StructureMap. Искате от контейнера да конвертира екземпляра на IWindow и след това да извика неговия метод Show. Върнатият екземпляр на IWindow е MainWindowAdapter, когато извикате неговия метод Show, той извиква капсулирания метод Show на MainWindow, който ставанакара желания прозорец да се покаже на потребителя.
WPF предоставя просто местоположение за корена на композицията. Всичко, което трябва да направите, е да премахнете StartupUri от App.xaml, да замените OnStartup в App.xaml.cs и да свържете приложението там.
Досега сте виждали примери, при които рамки осигуряват шевове, които ни позволяват да възприемем жизнения цикъл на екземпляри на ключови обекти (уеб страници, екземпляри на услуги, прозорци и други). В много случаи това е доста просто; но дори когато стане толкова сложно, като в WCF, например, пак можем да постигнем целта и да приложим истински механизъм за инжектиране на зависимост, без да правим компромис с нашите принципи.
Някои рамки обаче не ни дават този лукс.