我正在开发一个 WPF 应用程序,该应用程序使用声明来控制用户可以做什么和不可以做什么。要求是禁用用户无权访问的控件。在我们的基础 View 模型中,我们有以下方法:
HasClaim(string name);
我想在 View 中做这样的事情:
<button IsEnabled="{Binding HasClaim("NAME")}" />
我知道我可以创建一个 ObjectDataProvider,但我不想为每个声明都创建一个。
最佳答案
如果您经常在 View 中这样做,请考虑使用标记扩展。很可能您不需要来自 View 或 View 模型的任何信息来检查用户是否有正确的声明,通常此类信息是在登录时获取的并且不依赖于具体 View 。
public class HasClaimExtension : MarkupExtension {
private readonly string _name;
public HasClaimExtension(string name) {
_name = name;
}
public override object ProvideValue(IServiceProvider serviceProvider) {
return HasClaim();
}
private bool HasClaim() {
// check if user has this claim here
if (_name.ToLowerInvariant() == "admin")
return true;
return false;
}
}
然后只是:
<Button IsEnabled="{local:HasClaim Admin}" Height="20" Width="100"/>
在不太可能的情况下,您确实需要访问您的 View 模型,您仍然可以这样做:
public class HasClaimExtension : MarkupExtension {
private readonly string _name;
public HasClaimExtension(string name) {
_name = name;
}
public override object ProvideValue(IServiceProvider serviceProvider) {
var service = (IProvideValueTarget) serviceProvider.GetService(typeof (IProvideValueTarget));
// this is Button or whatever control you set IsEnabled of
var target = service.TargetObject as FrameworkElement;
if (target != null) {
// grab it's DataContext, that is your view model
var vm = target.DataContext as MyViewModel;
if (vm != null) {
return vm.HasClaim(_name);
}
}
return false;
}
}
public class MyViewModel {
public bool HasClaim(string claim) {
return false;
}
}
更新以在评论中回答您的问题。你可以这样做。假设一些简单的 LoginManager 类:
public class LoginManager {
public static LoginManager Instance = new LoginManager();
private LoginManager() {
}
public bool IsLoggedIn { get; private set; }
public void Login() {
// do something, then
IsLoggedIn = true;
OnLoggedIn?.Invoke();
}
public bool HasClaim(string name) {
if (!IsLoggedIn)
throw new Exception("Cannot check claim until logged in");
return true;
}
public event Action OnLoggedIn;
}
它有一些关于声明是否已经可用的指示,如果现在不可用,还有一个事件来通知这些声明何时可用。然后在您的标记扩展中,您首先检查声明是否在此处。如果是 - 已经返回结果。如果不是 - 返回 false,但在这些声明可用时订阅该事件。之后 - 用实际值更新目标属性。
public class HasClaimExtension : MarkupExtension {
private readonly string _name;
public HasClaimExtension(string name) {
_name = name;
}
public override object ProvideValue(IServiceProvider serviceProvider) {
if (LoginManager.Instance.IsLoggedIn) {
return LoginManager.Instance.HasClaim(_name);
}
// if not logged in yet
var service = (IProvideValueTarget) serviceProvider.GetService(typeof (IProvideValueTarget));
var target = service.TargetObject as FrameworkElement;
// this is dependency property you want to set, IsEnabled in this case
var targetProperty = service.TargetProperty as DependencyProperty;
if (target != null && targetProperty != null) {
if (targetProperty.PropertyType != typeof (bool)) {
// not boolean property - throw
throw new Exception("HasClaim extension should be applied to Boolean properties only");
}
// here, subscribe to event after which your claims are available
LoginManager.Instance.OnLoggedIn += () => {
// update target property
if (Application.Current.Dispatcher.CheckAccess())
target.SetValue(targetProperty, LoginManager.Instance.HasClaim(_name));
else {
Application.Current.Dispatcher.Invoke(() => {
target.SetValue(targetProperty, LoginManager.Instance.HasClaim(_name));
});
}
};
}
return false;
}
}
关于c# - WPF 将 IsEnabled 绑定(bind)到 View 模型上的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36557924/