MVVM UI控制用户相关的WPF可见性

时间:2012-05-18 13:41:20

标签: c# wpf mvvm menuitem

这更像是一个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;
        }
    }
}

我的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. 某些控件可能对多个角色可见(例如开发人员和业务分析师)
  3. 到目前为止我的两个选择

    1. 创建一个自定义转换器,该转换器具有根据允许的角色派生可见性的逻辑,并将其绑定到MenuItem.Visibility Value属性。这个问题:a)允许此控件的角色需要在运行时传递,因为它们来自数据库,您无法将CommandParameters绑定到角色集合。 b)转换器如何访问User.Role以获得可见性?
    2. 在我的UI模型中的属性内创建可见性逻辑(例如,MenuItemModel)。但是在这里我不想在User和MenuItemModel类之间创建依赖关系。
    3. 根据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);
          }
      

      这似乎工作正常

1 个答案:

答案 0 :(得分:4)

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

即,你可以在MainViewModel的构造函数中使用类似的东西:

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