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)
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>