ПРОГРАММИРОВАНИЕ16 августа
MVVM расшифровывается как Model-View-ViewModel, известный шаблон проектирования приложений, созданный Microsoft. Это позволяет разработчикам отделить логику пользовательского интерфейса от бизнес-правил. Например, пользовательская анимация или элементы рендеринга на экране будут вашей страницей. Тогда вызовы API для извлечения данных и загрузки этих данных в коллекции будут кодом ViewModel.
(Изображение предоставлено документацией Microsoft Xamarin)
Если вы не знакомы с шаблоном, вам следует ознакомиться с документацией Xamarin для MVVM, так как он подробно объясняет шаблон.
Рассмотрим этот простой пример ViewModel
, который хранит a Message
для отображения на экране для приложения Hello World.
1
2
3
4
|
public class MainViewModel { public string Message => "Hello World from MainViewModel!" ; } |
После ViewModel
создания нам не нужно делать с ним ничего особенного. Мы просто создаем его экземпляр или создаем новый в коде представления позади и назначаем его DataContext
.
1
2
3
4
5
6
7
8
|
public sealed partial class MainPage { public MainPage() { InitializeComponent(); DataContext = new MainViewModel(); } } |
Теперь вы можете обновить код представления для привязки к Message
свойству вместо жесткого кодирования строки в XAML.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
< Page x:Class = "ViewModelLocator.Views.MainPage" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d = "http://schemas.microsoft.com/expression/blend/2008" xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable = "d" > < Grid Background = "{ThemeResource ApplicationPageBackgroundThemeBrush}" > < TextBlock Text = "{Binding Message}" Margin = "20" FontSize = "30" /> </ Grid > </ Page > |
Локатор моделей представлений — это метод в приложениях MVVM для автоматического назначения представлению ViewModel
без DataContext
дополнительного кода, необходимого в представлении или коде позади. Этот метод становится все более полезным, когда вы хотите использовать внедрение зависимостей с вашим приложением MVVM. Это автоматически разрешит любые зависимости при создании ViewModel
экземпляра.
Мы не добавляем BasePage
и не переписываем основной код для решения этой проблемы. Мы собираемся использовать концепцию, известную как присоединенные свойства. Присоединенное свойство позволяет выполнять логику над открытыми членами объекта, которого нет в классе. Этот метод можно применять в конструкторе или в xaml. Если вы работали в приложениях Uno Platform, UWP или Xamarin.Forms, вы, скорее всего, использовали прикрепленное свойство и даже не знали об этом. Например, определение Grid
и определение строки или столбца с Grid.Row="0"
помощью прикрепленного свойства.
1
2
3
4
5
6
7
8
|
< Grid > < Grid.RowDefinitions > < RowDefinition Height = "Auto" /> </ Grid.RowDefinitions > < TextBlock Grid.Row = "0" Text = "This is using an attached property!" /> </ Grid > |
Если вы хотите узнать больше о прикрепленных свойствах, вам следует прочитать документацию Microsoft UWP по этому методу.
Мы собираемся сделать вещи немного в обратном направлении от большинства моих статей. Будет полезно увидеть окончательный API, прежде чем мы начнем.
Цель состоит в том, чтобы добавить 2 строки кода в наш XAML для регистрации файла ViewModelLocator
. Давайте запланируем определить ViewModelLocator
в пространстве имен Mvvm. Нам нужно будет добавить следующие строки в наше представление
ViewModelLocator.Mvvm
xmlns (пространство имен xml) в коде xaml. В нашем примере кода корневое пространство имен ViewModelLocator
ViewModelLocator
чтобы включить ее.
1
2
|
xmlns:mvvm= "using:ViewModelLocator.Mvvm" mvvm:ViewModelLocator.AutoWireViewModel= "True" |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
< Page x:Class = "ViewModelLocator.Views.MainPage" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d = "http://schemas.microsoft.com/expression/blend/2008" xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mvvm = "using:ViewModelLocator.Mvvm" mvvm:ViewModelLocator.AutoWireViewModel = "True" mc:Ignorable = "d" > < Grid Background = "{ThemeResource ApplicationPageBackgroundThemeBrush}" > < TextBlock Text = "{Binding Message}" Margin = "20" FontSize = "30" /> </ Grid > </ Page > |
Теперь, когда мы определили использование нашего API, мы можем создать, ViewModelLocator
который будет реализовывать прикрепленное свойство, чтобы назначить правильное ViewModel
значение DataContext
.
Давайте начнем с создания новой папки в вашем общем коде с именем Mvvm
, а затем создадим ViewModelLocator
класс.
Начните с создания базового прикрепленного свойства, которое включает в себя методы доступа, установки и измененного свойства.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public static class ViewModelLocator { public static DependencyProperty AutoWireViewModelProperty = DependencyProperty.RegisterAttached( "AutoWireViewModel" , typeof ( bool ), typeof (ViewModelLocator), new PropertyMetadata( false , AutoWireViewModelChanged)); public static bool GetAutoWireViewModel(UIElement element) { return ( bool )element.GetValue(AutoWireViewModelProperty); } public static void SetAutoWireViewModel(UIElement element, bool value) { element.SetValue(AutoWireViewModelProperty, value); } private static void AutoWireViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // TODO - Implement binding } } |
Это самый простой код, который вам понадобится для реализации прикрепленного свойства. При AutoWireViewModelChanged
вызове он будет передан на страницу в качестве параметра. Любой объект в платформе Uno, наследуемый от DependencyObject
которого является переданным базовым объектом. Это позволяет использовать более сложные сценарии, но мы сосредоточимся на простой привязке страницы.
Для этого нам нужно реализовать 2 шага
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
private static Type FindViewModel(Type viewType) { string viewName = string .Empty; if (viewType.FullName.EndsWith( "Page" )) { viewName = viewType.FullName .Replace( "Page" , string .Empty) .Replace( "Views" , "ViewModels" ); } var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName; var viewModelName = string .Format(CultureInfo.InvariantCulture, "{0}ViewModel, {1}" , viewName, viewAssemblyName); return Type.GetType(viewModelName); }
|
Наша логика здесь смотрит на текущую страницу или DependencyObject
пытается найти соответствующую ViewModel на основе имени. Например, наш код смотрит MainPage
и понимает наши конвенционные карты MainViewModel
. Оба этих класса содержат ключевое слово Main
, которое генерирует сопоставление. Затем метод использует отражение, чтобы получить тип объекта и вернуть его. Ваша FindViewModel
логика может отличаться, если у вас разные пространства имен, в которых существуют ваши ViewModels.
После того, как вы определили свой FindViewModel
метод, мы можем реализовать Bind
метод. Этот метод примет возвращаемый тип и создаст его экземпляр, который будет применен к DataContext
.
DependencyObject
является ли файл FrameworkElement
. Единственный DataContext
существует на, FrameworkElement
поэтому нам нужно убедиться, что у нас есть правильный тип.FineViewModel
метод и получите правильный тип ViewModel.DataContext
.
1
2
3
4
5
6
7
8
|
private static void Bind(DependencyObject view) { if (view is FrameworkElement frameworkElement) { var viewModelType = FindViewModel(frameworkElement.GetType()); frameworkElement.DataContext = Activator.CreateInstance(viewModelType); } }
|
В этом примере используется Activator
для создания нового экземпляра модели представления и является основным примером локатора модели представления. Чтобы расширить это, я бы обновил Activator
вызов, используя контейнер внедрения зависимостей, чтобы разрешить модель представления. Это гарантирует, что все зависимости будут внедрены без какой-либо дополнительной работы.
Если вы используете библиотеку Microsoft.Extensions.DependencyInjection, вы можете заменить Activator
вызов на ActivatorUtilities
. Мы не собираемся рассматривать внедрение зависимостей в этой статье, но если вы хотите еще больше расширить эту технику, вы можете найти этот фрагмент полезным.
1
|
frameworkElement.DataContext = ActivatorUtilities.GetServiceOrCreateInstance(((App)App.Current).Container, viewModelType); |
После того, как вы создали Bind
метод, вы можете обновить AutoWireViewModelChanged
реализацию, чтобы использовать этот Bind
метод. В качестве проверки безопасности я всегда гарантирую, что это новая переменная перед применением привязки, вы не хотите постоянно переустанавливать модель представления.
1
2
3
4
5
|
private static void AutoWireViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (( bool )e.NewValue) Bind(d); }
|
Теперь вы создали свой ViewModelLocator
код, чтобы убедиться, что ваш код правильный. Здесь я собрал полный код в одном фрагменте.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
public static class ViewModelLocator { public static DependencyProperty AutoWireViewModelProperty = DependencyProperty.RegisterAttached( "AutoWireViewModel" , typeof ( bool ), typeof (ViewModelLocator), new PropertyMetadata( false , AutoWireViewModelChanged)); public static bool GetAutoWireViewModel(UIElement element) { return ( bool )element.GetValue(AutoWireViewModelProperty); } public static void SetAutoWireViewModel(UIElement element, bool value) { element.SetValue(AutoWireViewModelProperty, value); } private static void AutoWireViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (( bool )e.NewValue) Bind(d); } private static void Bind(DependencyObject view) { if (view is FrameworkElement frameworkElement) { var viewModelType = FindViewModel(frameworkElement.GetType()); frameworkElement.DataContext = Activator.CreateInstance(viewModelType); } } private static Type FindViewModel(Type viewType) { string viewName = string .Empty; if (viewType.FullName.EndsWith( "Page" )) { viewName = viewType.FullName .Replace( "Page" , string .Empty) .Replace( "Views" , "ViewModels" ); } var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName; var viewModelName = string .Format(CultureInfo.InvariantCulture, "{0}ViewModel, {1}" , viewName, viewAssemblyName); return Type.GetType(viewModelName); } } |
На этом этапе вы можете запустить свое приложение, и ваша модель представления будет автоматически подключена к представлению. Любой новый созданный вами объект View/ViewModel будет связан простым добавлением двух операторов xaml, упомянутых ранее.
Это все, что вам нужно для настройки View Model Locator в вашем приложении MVVM. Целью локатора модели представления является автоматическое подключение вашего представления к модели представления. Прогрессом в методах здесь будет использование внедрения зависимостей в вашем локаторе модели представления для автоматического внедрения зависимостей в вашу модель представления.
Если у вас возникли проблемы с этой настройкой, обязательно ознакомьтесь с моим примером кода.
Источник What is MVVM
Диалог в WPF C#. Работа с файловой системой. Библиотека Оokii Dialogs WPF
MVVM - просто о сложном. Пример на C#
MVVM и внедрение зависимостей Часть 1
MVVM и внедрение зависимостей Часть 2
Теги: #с#, #mvvm, #программирование
Ваш комментарий успешно добавлен.
После проверки комментарий будет опубликован на сайте.