これは、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);
}
}
}
ここで、Menu を宣言してバインドする 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>
ここで私の要件:
- 一部の UI コントロール (MenuItems など) の可視性は、User.Role に依存します。例: MenuItemA は Role.Developer には表示されますが、Role.BusinessAnalyst には表示されません。
- 一部のコントロールは複数の役割で表示される場合があります (例: 開発者とビジネス アナリスト)
これまでの私の2つのオプション
- 許可されたロールに基づいて可視性を導出するロジックを持つカスタム コンバーターを作成し、それを MenuItem.Visibility Value プロパティにバインドします。これに関する問題: a) このコントロールに許可されたロールは、データベースから取得され、ロールのコレクションに CommandParameters をバインドできないため、実行時に渡す必要があります。b) 可視性を引き出すために、コンバーターは User.Role にどのようにアクセスできますか?
- UI モデル (MenuItemModel など) のプロパティ内に可視性ロジックを作成します。しかし、ここでは User クラスと MenuItemModel クラスの間に依存関係を作成したくありません。
密結合のシナリオ (依存関係) に遭遇することなく、User.Role に基づいて UI コントロールの可視性を動的に導出する最もクリーンな方法は何ですか?
ありがとう!
解決策:これが@fmunkertの提案に基づいて解決した方法です。「RemoveAll」メソッドにアクセスするには、MenuItemModel.Items プロパティを List に変更する必要があることに注意してください。
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
禁止された項目を再帰的に削除する my のメソッド:
/// <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);
}
これはうまく機能しているようです