Bind WPF control visibility using multiple variables from ViewModel

I am writing a WPF user control that displays a dynamically generated TabControl with multiple pages, each page, in turn, contains a list of dynamically generated controls (TextBox, Checkbox, etc.).

I want to set the visibility of TextBox, CheckBox controls based on whether the user has permission to view or not. This permission is a combination of the values ​​for each ViewModel ("VisiblyBy") control, as well as a property of the common UserMontrol ViewModel ("UserRole"). I just start working with WPF, but the standard method is to use ValueConvertor, however I don’t understand how I could write one that would combine / access different properties (VisiblyBy and UserRole), since they exist at different levels in my hierarchy ViewModel.

Here is the part of XAML where I get attached to the ViewModel:

<TabControl ItemsSource="{Binding VariableData.Pages}" SelectedIndex="0">
<!-- this is the header template-->
   <TabControl.ItemTemplate>
  <DataTemplate>
     <TextBlock Text="{Binding Title}" FontWeight="Bold"/>
  </DataTemplate>
</TabControl.ItemTemplate>
            
<!-- this is the tab content template-->
   <TabControl.ContentTemplate>
  <DataTemplate>
     <StackPanel>
        <ListBox  Grid.IsSharedSizeScope="True" 
                  ItemsSource="{Binding Variables}"
                  ItemTemplateSelector="{StaticResource templateSelector}">
        </ListBox>
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
           <Button Content="Cancel" />
               <Button Content="Submit" 
                       Command="{Binding DataContext.CommitChangesCommand, 
                                   RelativeSource={RelativeSource FindAncestor, 
                                   AncestorType={x:Type TabControl}}}" />
        </StackPanel>
    </StackPanel>
</DataTemplate>
  </TabControl.ContentTemplate>
</TabControl>
Run code

, , , , .

+4
4

IMultiValueConverter Multibinding.

<Window x:Class="ItemsControl_Learning.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ItemsControl_Learning"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <local:VisibilityConverter x:Key="conv" />
</Window.Resources>
<Grid>
    <Button Content="Test">
        <Button.Visibility>
            <MultiBinding Converter="{StaticResource conv}">
                <Binding  Path="Role" />
                <Binding Path="OtherProp" />                   
            </MultiBinding>
        </Button.Visibility>
    </Button>
</Grid>

public partial class MainWindow : Window
{

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new MainViewModel();
    }       
}

class MainViewModel
{
    private string role;
    public string Role
    {
        get { return role; }
        set { role = value; }
    }

    private string otherProp;
    public string OtherProp
    {
        get { return otherProp; }
        set { otherProp = value; }
    }
    public MainViewModel()
    {
        Role = "Admin";
        OtherProp = "Fail";
    }
}

class VisibilityConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (values[0].ToString().Equals("Admin") && values[1].ToString().Equals("Pass"))
        {
            return Visibility.Visible;
        }
        else
        {
            return Visibility.Collapsed;
        }
    }

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

Convert(...) values , MultiBinding.Bindings.

values[0] Role, values[1] OtherProp, , XAML

+7

ViewModel ( ) IsAuthorized . , , , , ( ).

, Role IsOther PropertyChange IsAuthorized prop. , Xaml .

public Visiblity IsAuthorized
{
   get { return (Role == "Admin" && OtherProp == "True") ? Visbility.Visible : Visibility.Hidden; }

}

 // When a value changes in Role or OtherProp fire PropertyChanged for IsAuthorized. 
public string Role
{
   get { return_Role;}
   set { _Role = value; 
         PropertyChanged("Role");
         PropertyChanged("IsAuthorized");
       }
}

public string OtherProp
{
   get { return_OtherProp;}
   set { _OtherProp = value; 
         PropertyChanged("OtherProp");
         PropertyChanged("IsAuthorized");
       }
}

, , , (-), , PropertyChanged .

+3

, MultiValueConverter, - , , .

public class AccessLevelToVisibilityConverter : MarkupExtension, IMultiValueConverter
{
    public object Convert( object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var visibility = Visibility.Hidden;

        var viewModel = (VariableDataViewModel)values[0];
        var item = (VariableDataItem)values[1];

        visibility = viewModel.IsVariableVisible(item);

        return visibility;
    }

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

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }
}

XAML:

<Grid.Visibility>
   <MultiBinding Converter="{p:AccessLevelToVisibilityConverter}" >
      <Binding Path="DataContext",  
               RelativeSource="{RelativeSource 
                                AncestorType=UserControl}" />
      <Binding Path="." />
   </MultiBinding>
</Grid.Visibility>

DataTemplates, , .

0

, Inversion of Control / ?

, , IOC, .


:

public enum SecurityLevels
{
    publicLevel = 0,
    userLevel,
    adminLevel
}

VM ( , ?) , , / . , VM IAuthorize.

public interface IAuthorize
{
    SecurityLevels CurrentLevel { get; set; }
    bool GetAuthorization(IAmIAuthorized instance);
}

IAmIAuthorized (Am-I-Authorized) , , . Injection Dependency DetermineAuthorizationFunc, ​​VM Injection Dependency:

public interface IAmIAuthorized
{
   SecurityLevels Level { get; }

   Visibility IsVisible { get; }

   bool IsAuthorized { get; }

   Func<IAmIAuthorized, bool> DetermineAuthorizationFunc { get; set; }
}

, , IAmIAuthorzied DetermineAuthorizationFunc, . , , DetermineAuthorizationFunc.

Xaml , , IsVisible Visibility VM .

 <TextBlock Text="{Binding Name}" Visibility="{Binding IsVisible}" />

IOC .

OP VariableDataItem :

public class VariableDataItem  : IAmIAuthorized
{
    public string Name { get; set; }

    public SecurityLevels Level { get; set; }

    public Func<IAmIAuthorized, bool> DetermineAuthorizationFunc { get; set; }

    public bool IsAuthorized
    {
        get { return DetermineAuthorizationFunc != null && 
                     DetermineAuthorizationFunc(this); }
    }

    public Visibility IsVisible
    {
        get { return IsAuthorized ? Visibility.Visible : Visibility.Hidden; }
    }    
}

VM

Holdings = new List<VariableDataItem>()
{
    new VariableDataItem() {Name = "Alpha", DetermineAuthorizationFunc = GetAuthorization, Level = SecurityLevels.publicLevel  },
    new VariableDataItem() {Name = "Beta",  DetermineAuthorizationFunc = GetAuthorization, Level = SecurityLevels.userLevel    },
    new VariableDataItem() {Name = "Gamma", DetermineAuthorizationFunc = GetAuthorization, Level = SecurityLevels.adminLevel   }
};

IOC:

public bool GetAuthorization(IAmIAuthorized instance)
{
    return (int)instance.Level <= (int)CurrentLevel; 
}

In this system, if the current state userLevel, our list will show only Alpha (which is publicly available) and Beta (which is a user), but not Gamma, which is only an administrator:

enter image description here

The administrator can see everything:

enter image description here

0
source

All Articles