I'm writing a WPF user control which 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 the TextBox, CheckBox controls based on whether the user has permission to view them or not. This permission is a combination of a value on each controls ViewModel ('VisiblyBy') and also a property of the overall UserControl ViewModel ('UserRole'). I'm just getting started on WPF but the standard method seems to be to use a ValueConvertor - however I don't understand how I could write one which would combine/access the different properties (VisiblyBy and UserRole) as they exist at different levels in my ViewModel hierary.
Here is part of the XAML where I bind 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>
I would also need to extend the number of variables that control visibility in the future as it would also depend where in the application it is used from.
You can try IMultiValueConverter and use 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();
}
}
In the Convert(...)
method, the order of the different inputs in the values
array is the same order as in the MultiBinding.Bindings
collection.
In this example values[0]
contains the Role
property and values[1]
will be OtherProp
because that is the order they got inserted in XAML
Bind to a composite property which holds the status instead without the need for a converter.
I would create a composite property on the ViewModel called IsAuthorized
and just bind to that property. For it always return the current status when the other properties are set.
How?
To accomplish the composite property the Role
and IsOther
properties also call PropertyChange
on the IsAuthorized
property; which always keeps the status fresh on the page.
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");
}
}
People seem to think that one has to only bind to a specific property(ies), but why make your life harder when a simply call to PropertyChanged with a composite property will do the job.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With