Question

I have a MVVM application that contains multiple views with some complex IsReadOnly rules based on user permissions, view/edit mode and object state.

I would like to set IsReadOnly and/or IsEnabled properties for entire groups of controls in the same container (GroupBox / StackPanel / Grid / UserControl / etc.). The value of this property will be defined in ViewModel.

I've got 3-6 different SomeGroupIsReadOnly properties per UserControl (with a large number of input controls like TextBox, RadioButtons, ComboBoxes and some DataGrids) and I'm looking for a generic, MVVM-friendly solution, that will allow me to reuse Bindings on per-container basis, instead of specifying them for each individual control separately.

How can I set IsReadOnly / IsEnabled on all controls inside container like Panel or GroupBox using XAML?

It doesn't seem that WPF supports this out of the box...

EDIT

I forgot to mention that setting IsEnabled for a container disables an important feature of TextBoxes - being able to copy their contents. I need them to be in IsReadOnly=true state. If there was a workarond for that, then my problem would be solved.

Was it helpful?

Solution

Something that's worked well for us is to define a view model that represents the permission structure of your application (YourPermissionsViewModel in my example below).

Then you could create a custom panel control that extends any panel of your choice (StackPanel in this example). That way you can add in the IsReadOnly property bindings and persist them to the panel's children.

Here's what the panel in XAML could look like:

<local:PanelExtension IsEnabled="{Binding YourPermissionsViewModel.IsEnabled}" 
                      IsReadOnly="{Binding YourPermissionsViewModel.IsReadOnly}">
    <TextBox Text="eeny" Width="100" />
    <TextBox Text="meeny" Width="100"/>
    <TextBox Text="miny" Width="100"/>
    <TextBox Text="mo" Width="100" />
    <Label Content="coolio" Width="100" />
</local:PanelExtension>

And here is the StackPanel extension control containing all of StackPanel's features as well as attaching a custom IsReadOnly dependency property that updates the corresponding property value on any child controls who possess that property:

public class PanelExtension : StackPanel
{
    public bool IsReadOnly
    {
        get { return (bool)GetValue(IsReadOnlyProperty); }
        set { SetValue(IsReadOnlyProperty, value); }
    }
    public static readonly DependencyProperty IsReadOnlyProperty =
        DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(PanelExtension),
        new PropertyMetadata(new PropertyChangedCallback(OnIsReadOnlyChanged)));

    private static void OnIsReadOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((PanelExtension)d).OnIsReadOnlyChanged(e);
    }

    protected virtual void OnIsReadOnlyChanged(DependencyPropertyChangedEventArgs e)
    {
        this.SetIsEnabledOfChildren();
    }

    public PanelExtension()
    {
        this.Loaded += new RoutedEventHandler(PanelExtension_Loaded);
    }

    void PanelExtension_Loaded(object sender, RoutedEventArgs e)
    {
        this.SetIsEnabledOfChildren();
    }

    private void SetIsEnabledOfChildren()
    {
        foreach (UIElement child in this.Children)
        {
            var readOnlyProperty = child.GetType().GetProperties().Where(prop => prop.Name.Equals("IsReadOnly")).FirstOrDefault();
            readOnlyProperty.SetValue(child, this.IsReadOnly, null);
        }
    }
}

With this approach you could add as many customized properties you need, it really lends you a great deal of flexibility and enables you to account for a multitude of scenarios you might encounter when you have to set your complex permissions on various elements.

OTHER TIPS

Disabling the Panel that contains your controls will also disable the controls inside the Panel. Bind the IsEnabled member of the Panel to a bool property on your ViewModel and set it according to your rules to disable the Panel and all of its children.

Xaml:

<GroupBox IsEnabled="{Binding Path=IsGroupEnabled}">
    <StackPanel>
        <Button Content="Sample Button"/>
        <TextBox Text="Sample text box"/>
    </StackPanel>
</GroupBox>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top