19

I would like to have a WPF container (panel, user control, etc.) that exposes a property to turn all children to read-only if set. This should pretty much be like setting a parent control to IsEnabled=false, which also disables all children. What children and which of their properties should be considered is fixed (e.g. TextBox.ReadOnly, DataGrid.ReadOnly, ...).

I have tried to create such a control, which essentially iterates all children of the visual tree (recursively) and deals with the controls accordingly.

This works fine, except for the case where further changes would affect the visual tree, so that new children are added. This is true for a ContentControl or ItemsControl. If children are added to the visual tree after I went though them, they obviously are not read-only.

I have bee trying to find a good event to react on (basically detect new children in the visual tree), but was unable to find something appropriate. UpdateLayout is fired, but way to often to go through the visual tree each time.

Is there a way to solve this? Is there probably another approach to getting all (relevant) children recursively set to read-only through a binding on a parent element?

(And no: I would not want to bind all children read-only properties to the global binding instead. The point is to have a single element/part that propagates this to all children)

user1211286
  • 681
  • 5
  • 17
  • I never really stepped back and thought of the "new visual children" issue. this is an excellent question, indeed. – Federico Berasategui Dec 03 '13 at 17:03
  • I guess your best bet is to have the ViewModel raise the property change notification (`NotifyPropertyChange(() => IsReadOnly);`) whenever a collection is modified or something, rather than trying to go the Visual Tree route – Federico Berasategui Dec 03 '13 at 17:07
  • You need to do it as a dependency property. Dependency property allows inheritance which is what you are looking for. IsEnabled is a dependency property, IsReadOnly is not. – user2880486 Dec 03 '13 at 17:17

1 Answers1

17

You may do this with an attached property that provides value inheritance:

public class ReadOnlyPanel
{
    public static readonly DependencyProperty IsReadOnlyProperty =
        DependencyProperty.RegisterAttached(
            "IsReadOnly", typeof(bool), typeof(ReadOnlyPanel),
            new FrameworkPropertyMetadata(false,
                FrameworkPropertyMetadataOptions.Inherits, ReadOnlyPropertyChanged));

    public static bool GetIsReadOnly(DependencyObject o)
    {
        return (bool)o.GetValue(IsReadOnlyProperty);
    }

    public static void SetIsReadOnly(DependencyObject o, bool value)
    {
        o.SetValue(IsReadOnlyProperty, value);
    }

    private static void ReadOnlyPropertyChanged(
        DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        if (o is TextBox)
        {
            ((TextBox)o).IsReadOnly = (bool)e.NewValue;
        }
        // other types here
    }
}

You would use it in XAML like this:

<StackPanel local:ReadOnlyPanel.IsReadOnly="{Binding IsChecked, ElementName=cb}">
    <CheckBox x:Name="cb" Content="ReadOnly"/>
    <TextBox Text="Hello"/>
</StackPanel>
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • 1
    I remember to have tried this approach initially, and it did not produce acceptable results in terms of performance, it IS a good solution, though. – Federico Berasategui Dec 03 '13 at 17:28
  • Of course there may be an impact on performance. From [MSDN](http://msdn.microsoft.com/en-us/library/ms753197.aspx#Making_a_Custom_Property_Inheritable): *Note, however, that designating a property as inheritable does have some performance considerations. In cases where that property does not have an established local value, or a value obtained through styles, templates, or data binding, an inheritable property provides its assigned property values to all child elements in the logical tree.* – Clemens Dec 03 '13 at 17:34
  • Very nice. I am struggeling with the performance impact, though. Did another post how to prevent the property from being propagated into a DataGrid, since I don't want individual cells to be read-only if I can switch the DataGrid to read-only. – user1211286 Dec 04 '13 at 15:06
  • This is very nice example.. but may I know can we set isreadonly through code.? – keerti_h Nov 16 '16 at 09:04