ПРОГРАММИРОВАНИЕ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.Mvvmxmlns (пространство имен xml) в коде xaml. В нашем примере кода корневое пространство имен ViewModelLocatorViewModelLocatorчтобы включить ее.|
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, #программирование
Ваш комментарий успешно добавлен.
После проверки комментарий будет опубликован на сайте.