Страницы

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

воскресенье, 15 декабря 2019 г.

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

#c_sharp #wpf #mvvm


Всем привет. Суть проблемы. Есть пользователи с разными ролями, например: админ,
модератор, юзер. 

Я хочу иметь возможность ограничить то, что пользователи могут видеть на основе их
роли. Т.е. на одной и той же View могут быть скрыты, заблокированы или же вовсе появится
новые элементы управления, в зависимости от роли текущего пользователя.

Как такую задачу решить максимально гибко и правильно в контексте паттерна MVVM?
    


Ответы

Ответ 1



На мой взгляд, имеет смысл создать иерархию моделей представления: 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

Ответ 2



Я думаю для каждого типа пользователей создать свой класс с набором необходимых полей.

Ответ 3



Можно добавить свойство Role во вьюмодель типа enum, во вьюхе биндить доступность контролов (IsEnabled, Visbility) к этому свойству. Если не хочется возится с конверторами и тригерами, можно во вьюмодель запихать свойства типа CanPressButtonX для простоты. В чем конкретно проблема то?

Ответ 4



Я сделал так: Получил список разрешенных элементов для роли (из бд, xml и т.д.) код foreach (var rule in rules) { var control = FindName(rule.ProgramModule) as Control; if (control != null) control.Visibility = Visibility.Visible; } где ProgramModule - это наименование элемента. Некоторая сложность в том, что необходимо все элементы делать collapsed (ну либо идти от обратного, делать все элементы разрешенными и выключать те, которые запрещены). Просто если пялиться в mvvm по фен-шую, то лепить "вьюхи" под каждую роль - но это очень избыточно, особенно если контроллов на форме "море".

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

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