2016-04-11 9 views
3

Ich arbeite an einer WPF-Anwendung, die Ansprüche für die Kontrolle verwendet, was Benutzer sind und nicht dürfen. Die Anforderung besteht darin, dass Steuerelemente, auf die Benutzer keinen Zugriff haben, deaktiviert sind. In unserer Basisansicht Modell haben wir die folgende Methode:WPF-Bindung IsEnabled zu Methode auf Ansichtsmodell

HasClaim(string name);

Ich mag so etwas wie dies in den Ansichten tun:

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

Ich weiß, ich könnte ein Object aber ich will nicht um einen für jeden Anspruch zu erstellen.

+0

Wie schaffen Sie die Tasten ... wir müssen wirklich mehr Code sehen ... – Monty

+0

Sie können nur an Eigenschaften binden, also ist die Lösung einfach: Erstellen Sie eine Eigenschaft, die 'HasClaim (" NAME ")' aufruft und an diese bindet. –

+0

@mike z: Wie würden Sie eine Eigenschaft mit Parameter aus der Sicht aufrufen? ... :-) Ich denke das ist der Converter wie Monty beschrieben. – Fruchtzwerg

Antwort

3

Wenn Sie dies häufig in Ihren Ansichten tun, sollten Sie die Markuperweiterung verwenden. Höchstwahrscheinlich benötigen Sie keine Informationen aus dem Ansichts- oder Ansichtsmodell, um zu überprüfen, ob der Benutzer den richtigen Anspruch hat. In der Regel werden solche Informationen beim Anmelden abgerufen und sind nicht von der konkreten Ansicht abhängig.

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

Dann einfach:

<Button IsEnabled="{local:HasClaim Admin}" Height="20" Width="100"/> 

Im unwahrscheinlichen Fall, dass Sie wirklich den Zugriff auf Ihr View-Modell benötigen, können Sie das noch tun:

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

UPDATE Ihre Frage in Kommentar antworten . Du kannst es so machen. Nehmen wir an ein paar einfache Loginmanager Klasse:

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

Es hat einige Hinweise darüber, ob Ansprüche sind bereits verfügbar, und auch ein Ereignis zu benachrichtigen, wenn diese Ansprüche zur Verfügung, wenn jetzt sind sie nicht. Dann überprüfen Sie zuerst in Ihrer Markup-Erweiterung, ob die Ansprüche hier sind. Wenn ja - einfach Ergebnis zurückgeben. Wenn nicht - Rückgabe false, aber Subribbe an das Ereignis, wenn diese Ansprüche verfügbar werden. Danach - Zielobjekt mit echtem Wert aktualisieren.

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

Ausgezeichnet! Das ist, was ich gesucht habe, danke. – Nick

+0

Eine Frage damit. Ist es möglich, dies erneut auszuführen? Der Grund, warum ich frage ist, weil in dieser Anwendung das Hauptfenster vor der Authentifizierung erstellt wird, also wird alles falsch zurückgegeben. – Nick

+0

Aktualisierte meine Antwort mit einem Vorschlag darüber, wie dies zu tun ist. – Evk

1

Sie können eine MultiValueConverter verwenden und übergeben Sie den Knopf selbst und seine Datacontext als Bindungen:

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

Und die Konverter-Klasse:

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(); 
    } 
} 
Verwandte Themen