Святой длинный вопрос, Бэтмен!
1:
Процесс будет работать, я не знаю, как использовать LoginModel
для разговора с MainWindowViewModel
.
Вы можете попробовать что-то вроде LoginView -> LoginViewModel -> [SecurityContextSingleton || LoginManagerSingleton] -> MainWindowView
Я знаю, что некоторые синглтоны считаются анти-паттернами, но я считаю, что это проще всего сделать в подобных ситуациях. Таким образом, синглтон-класс может реализовывать интерфейс INotifyPropertyChanged
и генерировать события всякий раз, когда обнаруживается событие login \ out.
Реализация LoginCommand
на LoginViewModel
или Singleton (лично я, вероятно, реализовал бы это на ViewModel
, чтобы добавить степень разделения между служебными классами ViewModel и "back-end"). Эта команда входа в систему вызовет метод на синглтоне для выполнения входа в систему.
2:
В этих случаях у меня обычно есть (еще один) синглтон-класс, действующий как PageManager
или ViewModelManager
. Этот класс отвечает за создание, размещение и хранение ссылок на страницы верхнего уровня или CurrentPage (только в одностраничной ситуации).
Мой класс ViewModelBase
также имеет свойство для хранения текущего экземпляра UserControl, который отображает мой класс, поэтому я могу подключать события Loaded и Unloaded. Это дает мне возможность иметь виртуальные OnLoaded(), OnDisplayed() and OnClosed()
методы, которые можно определить в ViewModel
, чтобы страница могла выполнять действия по загрузке и выгрузке.
Поскольку MainWindowView отображает экземпляр ViewModelManager.CurrentPage
, как только этот экземпляр изменяется, запускается событие Unloaded, вызывается метод Dispose моей страницы, и в конце концов GC
входит и приводит в порядок остальные.
Q3:
Я не уверен, понимаю ли я это, но, надеюсь, вы просто имеете в виду «Отображать страницу входа, когда пользователь не вошел в систему», если это так, вы можете указать своему ViewModelToViewConverter
игнорировать любые инструкции, когда пользователь не вошел в систему в (проверяя синглтон SecurityContext) и вместо этого показывает только шаблон LoginView
, это также полезно в тех случаях, когда вы хотите, чтобы страницы, на которые имеют право только определенные пользователи, могли просматривать или использовать, где вы можете проверить требования безопасности перед созданием представления, и замените его запросом безопасности.
Извините за длинный ответ, надеюсь, это поможет:)
Edit:
Также вы ошиблись "Управление"
<ч />
Изменить для вопросов в комментариях
Как LoginManagerSingleton будет общаться напрямую с
MainWindowView. Не должно ли все пройти через
MainWindowViewModel, чтобы на
MainWindowView
Извините, чтобы уточнить - я не имею в виду, что LoginManager напрямую взаимодействует с MainWindowView (как это должно быть просто для просмотра), а скорее, что LoginManager
просто устанавливает свойство CurrentUser в ответ на вызов, который выполняется LoginCommand, который, в свою очередь, вызывает событие PropertyChanged, и MainWindowView (который прослушивает изменения) реагирует соответствующим образом.
LoginManager может затем вызвать PageManager.Open(new OverviewScreen())
(или PageManager.Open("overview.screen")
, когда вы внедрили IOC), например, чтобы перенаправить пользователя на экран по умолчанию, который пользователи видят после входа в систему.
LoginManager, по сути, является последним этапом реального процесса входа в систему, и представление просто отражает это соответствующим образом.
Кроме того, при наборе этого слова мне пришло в голову, что вместо использования синглтона LoginManager все это может быть размещено в классе PageManager
. Просто есть метод Login(string, string)
, который устанавливает CurrentUser при успешном входе в систему.
Я понимаю идею PageManagerView, в основном через PageManagerViewModel
Я бы не спроектировал PageManager в дизайне View-ViewModel, просто обычный домашний синглтон, который реализует INotifyPropertyChanged
, должен сделать свое дело, так что MainWindowView может реагировать на изменение свойства CurrentPage.
Является ли ViewModelBase абстрактным классом, который вы создали?
Да. Я использую этот класс в качестве базового класса для всех моих ViewModel.
Этот класс содержит
- Свойства, которые используются на всех страницах, такие как Title, PageKey и OverriddenUserContext.
- Распространенные виртуальные методы, такие как PageLoaded, PageDisplayed, PageSaved и PageClosed
- Реализует INPC и предоставляетзащищенный метод OnPropertyChanged, используемый для вызова события PropertyChanged
- и предоставляет скелетные команды для взаимодействия со страницей, такие как ClosePageCommand, SavePageCommand и т. д.
Когда обнаружен вход в систему,Для CurrentControl установлено новое представление
Лично я бы удерживал только экземпляр ViewModelBase, который отображается в данный момент.На это затем ссылается MainWindowView в ContentControl следующим образом: Content="{Binding Source={x:Static vm:PageManager.Current}, Path=CurrentPage}"
.
Затем я также использую конвертер для преобразования экземпляра ViewModelBase в UserControl, но это не обязательно;Вы можете просто полагаться на записи ResourceDictionary, но этот метод также позволяет разработчику перехватывать вызов и отображать SecurityPage или ErrorPage, если требуется.
Затем, когда приложение запускается, оно обнаруживает, что никто не вошел в систему,и, таким образом, создает LoginView и устанавливает его как CurrentControl.Вместо того чтобы утверждать, что LoginView отображается по умолчанию
Вы можете создать приложение так, чтобы первая страница, отображаемая пользователю, была экземпляром OverviewScreen.Который, поскольку PageManager в настоящее время имеет нулевое свойство CurrentUser, ViewModelToViewConverter будет перехватывать это и вместо того, чтобы отображать OverviewScreenView UserControl, он вместо этого будет отображать UserView UserControl LoginView.
Если и когда пользователь успешно войдет в систему,LoginViewModel будет указывать PageManager перенаправить на исходный экземпляр OverviewScreen, на этот раз правильно отображая, поскольку свойство CurrentUser не равно нулю.
Как люди обходят это ограничение, как вы упоминаете, как другие, одиночные игрыплохи
Я с тобой в этом, мне нравится хороший синглтон.Однако их использование должно быть ограничено для использования только там, где это необходимо.Но, на мой взгляд, они имеют вполне обоснованное применение, но не уверены, что кто-то еще хочет вмешаться в это дело?
Редактировать 2:
Используете ли вы общедоступный каркас / набор классов для MVVM
Нет, я используюрамки, которые я создал и усовершенствовал за последние двенадцать месяцев или около того.Каркас по-прежнему следует большинству рекомендаций MVVM, но включает в себя некоторые личные штрихи, которые уменьшают объем кода, который требуется написать.
Например, некоторые примеры MVVM устанавливают свои представлениятак же, как у вас;Принимая во внимание, что View создает новый экземпляр ViewModel внутри своего свойства ViewObject.DataContext.Для некоторых это может хорошо работать, но не позволяет разработчику перехватывать определенные события Windows из ViewModel, такие как OnPageLoad ().
OnPageLoad () в моем случае вызывается после того, как все элементы управления на странице созданы и открыты для просмотра на экране, что может быть мгновенно, через несколько минут после вызова конструктора или никогдавсе.Именно здесь я выполняю большую часть своей загрузки данных, чтобы ускорить процесс загрузки страниц, если на этой странице есть несколько дочерних страниц внутри вкладок, которые, например, в настоящее время не выбраны.
Но не только это, путем создания ViewModelтаким образом увеличивается количество кода в каждом представлении минимум на три строки.Это может показаться не так много, но эти строки кода не только одинаковы для всех представлений, создающих дублирующийся код, но дополнительный счетчик строк может сложиться довольно быстро, если у вас есть приложение, которое требует много представлений,Это, и я действительно ленивый .. Я не стал разработчиком для ввода кода.
Что я буду делать в будущем пересмотре вашей идеи страницыДиспетчер должен иметь несколько открытых одновременно окон, например, tabcontrol, где менеджер страниц управляет вкладками, а не одним userControl.Затем вкладки могут быть выбраны отдельным представлением, связанным с менеджером страниц
В этом случае PageManager не нужно будет содержать прямую ссылку на каждый из открытых классов ViewModelBase, только те, которые находятся вверхний уровень.Все остальные страницы будут дочерними по отношению к своим родителям, чтобы дать вам больший контроль над иерархией и позволить вам проследить события Save и Close.
Если вы поместите их в свойство ObservableCollection<ViewModelBase>
в PageManager, вытолько тогда потребуется создать TabControl в MainWindow, чтобы его свойство ItemsSource указывало на свойство Children в PageManager, а остальное выполнял механизм WPF.
Можете ли вы немного больше расширить ViewModelConverter
Конечно, чтобы дать вам общее представление, было бы легче показать некоторый код.
public override object Convert(object value, SimpleConverterArguments args)
{
if (value == null)
return null;
ViewModelBase vm = value as ViewModelBase;
if (vm != null && vm.PageTemplate != null)
return vm.PageTemplate;
System.Windows.Controls.UserControl template = GetTemplateFromObject(value);
if (vm != null)
vm.PageTemplate = template;
if (template != null)
template.DataContext = value;
return template;
}
Читая этот код в разделах, которые он читает:
- Если значение равно нулю, вернуть.Простая проверка нулевой ссылки.
- Если значением является ViewModelBase, и эта страница уже загружена, просто верните это представление.Если вы этого не сделаете, вы будете создавать новый просмотр каждый раз, когда отображается страница, и вызывать непредвиденное поведение.
- Получить шаблон страницы UserControl (показан ниже)
- Установитьсвойство PageTemplate, чтобы этот экземпляр можно было подключить, и чтобы мы не загружали новый экземпляр при каждом проходе.
- Установите View DataContext для экземпляра ViewModel, эти две строки полностью заменяют те три строки, о которых я говорило более ранних с каждом виде с этого момента.
возвращает шаблон.Затем он будет отображаться в ContentPresenter для просмотра пользователем.
public static System.Windows.Controls.UserControl GetTemplateFromObject(object o)
{
System.Windows.Controls.UserControl template = null;
try
{
ViewModelBase vm = o as ViewModelBase;
if (vm != null && !vm.CanUserLoad())
return new View.Core.SystemPages.SecurityPrompt(o);
Type t = convertViewModelTypeToViewType(o.GetType());
if (t != null)
template = Activator.CreateInstance(t) as System.Windows.Controls.UserControl;
if (template == null)
{
if (o is SearchablePage)
template = new View.Core.Pages.Generated.ViewList();
else if (o is MaintenancePage)
template = new View.Core.Pages.Generated.MaintenancePage(((MaintenancePage)o).EditingObject);
}
if (template == null)
throw new InvalidOperationException(string.Format("Could not generate PageTemplate object for '{0}'", vm != null && !string.IsNullOrEmpty(vm.PageKey) ? vm.PageKey : o.GetType().FullName));
}
catch (Exception ex)
{
BugReporter.ReportBug(ex);
template = new View.Core.SystemPages.ErrorPage(ex);
}
return template;
}
Это код в конвертере, который выполняет большую часть основной работы, читая разделы, которые вы можетесм .:
- Основной блок try..catch, используемый для перехвата любых ошибок конструирования классов, включая
- Страница не существует,
- Ошибка времени выполнения в коде конструктора
- И фатальные ошибки в XAML.
- convertViewModelTypeToViewType () просто пытается найти представление, соответствующее ViewModel, и возвращает код типа, который, по его мнению, должен быть (это может быть нулем).
- Если это не NULL, создайте новый экземпляр типа.
- Если нам не удается найти представление для использования, попробуйте создать страницу по умолчанию для этого типа ViewModel.У меня есть несколько дополнительных базовых классов ViewModel, которые наследуются от ViewModelBase, которые обеспечивают разделение обязанностей между типами страниц, которыми они являются.
- Например, класс SearchablePage просто отобразит список всех объектов в системе определенного типа и предоставит команды Add, Edit, Refresh и Filter.
- MaintenancePage будет получатьполный объект из базы данных, динамически генерировать и позиционировать элементы управления для полей, которые предоставляет объект, создает дочерние страницы на основе любой коллекции, имеющейся у объекта, и предоставляет команды «Сохранить» и «Удалить» для использования.
- Если у нас все еще нет шаблона для использования, выведите ошибку, чтобы разработчик знал, что что-то пошло не так.
- В блоке catch любая возникающая ошибка во время выполнения отображается пользователю в видеFriendly ErrorPage.
Все это позволяет мне сосредоточиться только на создании классов ViewModel, поскольку приложение будет просто отображать страницы по умолчанию, если только страницы View не были явно переопределены разработчиком для этой ViewModel.