c# - WPF 将 IsEnabled 绑定(bind)到 View 模型上的方法

标签 c# wpf data-binding

我正在开发一个 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/

相关文章:

带绑定(bind)的 WPF 默认值(使用样式触发器)

java - Spring MVC 数据绑定(bind) - 原始类型

JSF 与 setValueExpression 的绑定(bind)是只读的吗?

c# - 装箱/拆箱 - 只有值类型? Ref.types - 类型转换?

c# - 使用 C#/VBScript 获取 mdb 文件 (msaccess) 中的所有源代码

c# - 经典 Windows 主题和 Aero 中 WPF 控件的大小

xml - Delphi XE 中的数据绑定(bind)向导 - 可以将其配置为映射到 MSXML 接口(interface)吗?

c# - 对于 C# 中的用户控件,我收到类似 "constructor on type ' system.string' cannot found"的错误?

c# - Visual Studio 2012 拒绝显示源代码,即使加载了 PDB

WPF,滚动条拇指向后显示