Страницы

Поиск по вопросам

пятница, 26 октября 2018 г.

Отображение View по-разному в зависимости от роли пользователя WPF

Всем привет. Суть проблемы. Есть пользователи с разными ролями, например: админ, модератор, юзер.
Я хочу иметь возможность ограничить то, что пользователи могут видеть на основе их роли. Т.е. на одной и той же View могут быть скрыты, заблокированы или же вовсе появится новые элементы управления, в зависимости от роли текущего пользователя.
Как такую задачу решить максимально гибко и правильно в контексте паттерна MVVM?


Ответ

На мой взгляд, имеет смысл создать иерархию моделей представления:
abstract class BaseRoleVm { ... } class UserRoleVm : BaseRoleVm { ... } class ModeratorRoleVm : BaseRoleVm { ... } class AdminRoleVm : BaseRoleVm { ... }
Затем для каждой неабстрактной ViewModel создать по представлению (для переиспользования кода в представлении можно использовать стили, контролы, словари ресурсов).
В основном окне нужно будет завести свойство типа BaseRoleVm, которому будет присваиваться нужный наследник:
class MainVm { public BaseRoleVm Role { get; set; }
void UseAdminRole() { Role = new AdminRoleVm(); } ... }
Такой подход позволит легко вносить изменения в представления/модели представления ролей, обеспечит их независимость друг от друга, предоставить возможность добавлять/удалять роли без вмешательства в логику работы основного окна.
UPD
Вот небольшой пример на основе словарей ресурсов:
Модели представления (лежат в папке ViewModels):
public abstract class BaseRoleVm : BaseVm { public abstract string Name { get; } }
public class AdminRoleVm : BaseRoleVm { public override string Name { get { return "Админ"; } } }
public class UserRoleVm : BaseRoleVm { public override string Name { get { return "Юзер"; } } }
public class MainVm : BaseVm { public BaseRoleVm SelectedRole { get { return _selectedRole; } set { SetField(ref _selectedRole, value); } } private BaseRoleVm _selectedRole;
public BaseRoleVm[] Roles { get; }
public MainVm() { // формировать список или создавать нужную модель представления можно через MEF или Reflection. // тогда вы не будете зависеть от перечня ролей Roles = new BaseRoleVm[] { new AdminRoleVm(), new UserRoleVm(), };
SelectedRole = Roles.FirstOrDefault(); } }
Представления (лежат в папке Views)
// содержимое файла "AdminRole.xaml" (тип Resource Dictionary)
// содержимое файла "UserRole.xaml" (тип Resource Dictionary)
// главное окно // содержимое файла "Main.xaml" (тип Window)
И простейший селектор шаблонов:
public class ViewModelTemplateSelector : DataTemplateSelector { public override DataTemplate SelectTemplate(object item, DependencyObject container) { if (item == null) { return null; }
var itemType = item.GetType();
var resourceDictonary = new ResourceDictionary { Source = new Uri(string.Format( "pack://application:,,,/{0};component/Views/{1}.xaml", itemType.Assembly.FullName, itemType.Name.Replace("Vm", string.Empty))) }; return resourceDictonary .Values .OfType() .SingleOrDefault(_ => ReferenceEquals(_.DataType, itemType)); } }
На уровне представления роли никак не связаны между собой. ContentControl использует написанный ViewModelTemplateSelector для поисков нужных шаблонов. Происходит примерно следующее:
У ContentControl меняется Content (сработало PropertyChanged) ContentControl вызывает у ViewModelTemplateSelector метод SelectTemplate и передает туда Content в качестве item ViewModelTemplateSelector находит файл с представлением на основе имени типа и достает оттуда DataTemplate ContentControl отображает Content с использованием найденного DataTemplate

Комментариев нет:

Отправить комментарий