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 для створення вікна. У наступному прикладі використовується Poor Man's DI для того, щоб проілюструвати, що вам не доводиться покладатися на можливості якогось конкретного DI-контейнера.

Приклад: приєднання цінного клієнта керування товарами

У попередньому прикладі розроблявся веб-сервіс, який ми можемо використовувати для керування каталогом товарів у шаблоні Commerce. У цьому прикладі ми створимо WPF програму, яка використовує цей веб-сервіс для управління товарами. Малюнок 7-10 демонструє скріншот цієї програми.

Додаток реалізує Model View ViewModel (MVVM) підхід і містить три рівні, якіпродемонстровані малюнку 7-11. Зазвичай ми тримаємо ту складову, в якій знаходиться більша частина логіки, ізольовано від інших модулів – в даному прикладі PresentationLogic.ProductManagementClient – ​​це humble-виконавець (humble executable), який виконує дещо більшу роботу, ніж просто визначає інтерфейс користувача і делегує реалізацію іншим модулям .

Завдяки підходу MVVM ми передаємо ViewModel як DataContext головного вікна, а механізм зв'язування даних і движок шаблонизації даних дбають про коректне представлення даних, як тільки ми вплітаємо нові ViewModels або змінюємо дані існуючих ViewModels .

MVVM

Model View ViewModel (MVVM) – це патерн проектування, для якого чудово підходить WPF. Він поділяє код інтерфейсу користувача на три окремі відповідальності.

Model – це основна модель програми. Нею часто, але не завжди є доменна модель. Вона часто складається з POCO-об'єктів. У цьому прикладі доменна модель реалізується у веб-сервісі, тому на цьому рівні у вас немає справжньої доменної моделі. Тим не менш, додаток функціонує з абстракціями, що знаходяться поверх проксі веб-сервісу, і це ваша модель. Зверніть увагу на те, що Model зазвичай виражається UI-нейтральним способом. Це не передбачає, що Model буде розкрита безпосередньо інтерфейсом користувача, тому вона не розкриває ніякої WPF-специфічної функціональності.

View - це розглянутий нами інтерфейс користувача. У WPF ми можемо офіційно виразити View в XAML і використовувати механізм зв'язування даних та движок шаблонизації даних для представлення даних. Можна виразити Views без використання виділеного коду.

ViewModel – міст між View та Model.Кожен ViewModel – це клас, який перетворює та розкриває Model конкретним специфічним способом. У WPF це означає, що ViewModel може розкривати списки як ObservableCollections тощо.

Впровадження залежностей у головний ViewModel

MainWindow містить тільки XAML розмітку і не містить жодного виділеного користувача коду. Зате він використовує механізм зв'язування даних для відображення даних на екрані і управління командами користувача. Для того щоб це дозволити ми повинні передати MainWindowViewModel у його властивість DataContext.

MainWindowViewModel розкриває такі дані, як список товарів, а також команди створення, оновлення або видалення товару. Можливість такої функціональності залежить від сервісу, який забезпечує доступ до каталогу товарів: абстракція IProduct ManagementAgent.

Крім IProductManagementAgent для MainWindowViewModel також необхідний сервіс, який він може використовувати для того, щоб контролювати його віконне середовище (windowing environment), наприклад, демонстрацію модальних діалогових вікон. Ця залежність називається IWindow.

MainWindowViewModel використовує патерн Constructor Injection з наступною сигнатурою конструктора:

Для з'єднання програми ми повинні створити 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 , який може використовуватися як шв, який, у свою чергу, дає нам велику гнучкість у тих ситуаціях, коли справа доходить до створення екземплярів Views на підставі розмітки.

Малюнок 7-12 демонструє остаточну діаграму залежностей програми.

Тепер, коли ви ідентифікували всі будівельні блоки, ви можете скомпонувати їх. Для того щоб зберегти Poor Man's DI код симетричним, використовуючи прице DI-контейнер, я реалізував це як Resolve метод спеціалізованого класу контейнера. У наступному лістингу продемонстровано реалізацію.

Зрештою ви повертаєте екземпляр IWindow, реалізований MainWindowAdapter, а для цього вам потрібні WPF Window та IMainWindowViewModelFactory. Першим вікном, яке ви повинні продемонструвати користувачам, має бути MainWindow, тому саме його ви і передаєте в MainWindowAdapter.

MainWindowViewModelFactory використовує патерн Constructor Injection для запиту IProductManagementAgent , тому ви повинні скомпонувати WcfProductManagementAgent з двома його залежностями.

Остаточний MainWindowAdapter, що повертається з методу, обгортає MainWindow, тому, коли ми викликаємо метод Show, він делегує повноваження методу Show MainWindow. Це саме те, що ви і робитимете в Composition Root.

Реалізація Composition Root

Тепер, коли ви знаєте, як підключити додаток, вам потрібно лише зробити це в правильному місці. Як описувалося в попередньому розділі, вам спочатку потрібно відкрити App.xaml і видалити атрибут StartupUri , оскільки ви хочете самостійно явним чином компонувати початкове вікно запуску.

Після того, як ви це зробили, вам потрібно лише перевизначити метод OnStartup у App.xaml.cs та викликати контейнер.

У цьому прикладі ви використовуєте спеціалізований ProductManagementClientContainer, але ви також могли використовувати і універсальний DI-контейнер, наприклад Unity або StructureMap. Ви просите контейнер перетворити екземпляр IWindow, а потім викликати його метод Show. Повертаний екземпляр IWindow – це MainWindowAdapter , коли ви викликаєте його метод Show, він викликає метод Show інкапсульованого MainWindow, який стаєпричиною того, що бажане вікно демонструється користувачеві.

WPF пропонує просте місце для Composition Root. Все, що вам потрібно зробити - видалити StartupUri з App.xaml, перевизначити OnStartup в App.xaml.cs і скомпонувати програму.

До цього моменту ви зустрічалися з прикладами, в яких фреймворки надають шви, що дозволяють нам запозичити життєвий цикл екземплярів ключових об'єктів (веб-сторінок, екземплярів сервісів, вікна та інших). У багатьох випадках це просто; але навіть коли це настільки ускладнюється, як, наприклад, у WCF, ми все ще можемо досягти мети і реалізувати справжній механізм впровадження залежностей, не наражаючи при цьому на ризик наші принципи.

Деякі фреймворки, проте, не надають нам такої розкоші.