簡體   English   中英

WPF將IsEnabled綁定到視圖模型上的方法

[英]WPF bind IsEnabled to method on view model

我正在使用WPF應用程序,該應用程序使用聲明來控制用戶是什么,不允許做什么。 要求是禁用用戶無權訪問的控件。 在我們的基本視圖模型中,我們有以下方法:

HasClaim(string name);

我想在視圖中做這樣的事情:

<button IsEnabled="{Binding HasClaim("NAME")}" />

我知道我可以使用ObjectDataProvider,但我不想為每個聲明創建一個。

如果您經常在視圖中執行此操作,請考慮使用標記擴展。 您很可能不需要查看或查看模型中的任何信息來檢查用戶是否具有正確的聲明,通常這些信息是在登錄時獲取的,並且不依賴於具體視圖。

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"/>

在不太可能的情況下,您確實需要訪問您的視圖模型,您仍然可以這樣做:

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;
    }
}

您可以使用MultiValueConverter並將Button本身及其DataContext作為綁定傳遞:

<Button Name="someName">
    <Button.IsEnabled>
        <MultiBinding Converter={StaticResource HasClaimConverter}>
            <Binding Path="Name" RelativeSource="{RelativeSource Self}"/>
            <Binding/>
        </MultiBinding>
    </Button.IsEnabled>
</Button>

轉換器類:

class HasClaimConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var name= values[0] as String;
        var vm = values[1] as YourViewModel;

        return YourViewModel.HasClaim(name);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM