MVVM UI 控件依赖于用户与 WPF 的可见性

本文关键字:WPF 可见性 用户 UI 控件 依赖于 MVVM | 更新日期: 2023-09-27 18:34:31

这更像是一个MVVM应用程序设计问题。我正在尝试实现一个非常基本的基于用户的权限管理,其中应用程序中某些控件的可见性派生自当前用户角色。这是我的简化模型:

public class User
{
 public string UserName {get;set;}
 public Role UserRole {get;set;}
}
public enum Role
{
  Developer,
  BusinessAnalyst
}
public class MenuItemModel
{
   public ICommand Command {get;set;}
   public string Header {get;set;}
   public bool Visible {get;set;}
   private List<MenuItemModel> _Items;
   public List<MenuItemModel> Items
   {
        get { return _Items ?? (_Items = new List<MenuItemModel>()); }
        set
        {
            _Items = value;
        }
    }
}

My MainViewModel 包含以下属性:

public class MainViewModel : ViewModelBase<MainViewModel>
{
    private ObservableCollection<MenuItemModel> _MainMenu;
    public ObservableCollection<MenuItemModel> MainMenu
    {
        get { return _MainMenu; }
        set
        {
            _MainMenu = value;
            NotifyPropertyChanged(x=>x.MainMenu);
        }
    }
    private User _CurrentUser;
    public User CurrentUser
    {
        get { return _CurrentUser; }
        set { 
            _CurrentUser = value;
            NotifyPropertyChanged(x=>x.CurrentUser);
        }
    }
}

这是我的 XAML,我在其中声明并绑定我的菜单:

<Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=MainMenu}">
            <Menu.ItemContainerStyle>
                <Style>
                    <Setter Property="MenuItem.Header" Value="{Binding Path=Header}"/>
                    <Setter Property="MenuItem.ItemsSource" Value="{Binding Path=Items}"/>
                    <!-- How to bind Visibility????-->
                    <!--Setter Property="MenuItem.Visibility" /-->
                </Style>
             </Menu.ItemContainerStyle>
         </Menu>

现在我的要求在这里:

  1. 某些 UI 控件(例如 MenuItems(的可见性取决于 User.Role。例如:MenuItemA 应该对 Role.Developer 可见,但对 Role.BusinessAnalyst 不可见。
  2. 某些控件可能对多个角色可见(例如开发人员和业务分析师(

到目前为止我的两个选择

  1. 创建一个自定义转换器,该转换器具有基于允许的角色派生可见性的逻辑,并将其绑定到 MenuItem.可见性值属性。这样做的问题:a(此控件允许的角色需要在运行时传递,因为它们来自数据库,并且您无法将命令参数绑定到角色集合。b(转换器如何访问User.Role以获得可见性?
  2. 在我的 UI 模型(例如 MenuItemModel(中的属性中创建可见性逻辑。但在这里,我不想在我的用户和 MenuItemModel 类之间创建依赖关系。
基于

User.Role 动态派生 UI 控件可见性而不会遇到紧密耦合的方案(依赖项(的最干净方法是什么?

谢谢!

解决方案:这就是我根据@fmunkert建议最终解决它的方式。请注意,我必须将 MenuItemModel.Items 属性更改为 List 才能访问"RemoveAll"方法:

    public MainViewModel()
    {
        //Create a new User with a Role
        InitializeUser();
        //Get all the Menus in the application
        List<MenuItemModel> allItems = GetAllMenus();
        //Remove recursively all Items that should not be visible for this user
        allItems.RemoveAll(x=>!IsVisibleToUser(x));
        //Set my MainMenu based on the filtered Menu list
        _MainMenu = new ObservableCollection<MenuItemModel>(allItems);
    }
    private void InitializeUser()
    {
        CurrentUser = new User {UserName = "apsolis", UserRole = Role.Developer};
    }

这是我MainViewModel中递归删除禁止项目的方法:

    /// <summary>
    /// Method to check if current MenuItem is visible to user
    /// and remove items that are forbidden to this user
    /// </summary>
    /// <param name="m"></param>
    /// <returns></returns>
    public bool IsVisibleToUser(MenuItemModel m)
    {
        if (m.Items != null && m.Items.Count > 0)
        {
            m.Items.RemoveAll(y=>!IsVisibleToUser(y));
        }
        return m.Roles == null || m.Roles.Contains(CurrentUser.UserRole);
    }

这似乎工作正常

MVVM UI 控件依赖于用户与 WPF 的可见性

由于您是在 ViewModel 中生成菜单项,因此我建议根本不使用MenuItem.Visibility。相反,请让 ViewModel 确定允许用户查看哪些菜单项,并仅使用该菜单项子集填充 MainMenu 集合。即您的 MainViewModel 必须知道允许用户查看哪些菜单项。

也就是说,您将在 MainViewModel 的构造函数中使用类似的东西:

_MainMenu = new ObservableCollection<MenuItemModel>(_allMenuItems.Select(m => IsVisibleToUser(m)));