Контейнер WPF, чтобы все дочерние элементы управления были доступны только для чтения

Я хотел бы иметь контейнер WPF (панель, пользовательский элемент управления и т.д.), который предоставляет свойство, чтобы все дети были доступны только для чтения, если они установлены. Это должно быть похоже на установку родительского элемента управления IsEnabled = false, который также отключает всех дочерних элементов. Какие дети и какие из их свойств следует учитывать, фиксированы (например, TextBox.ReadOnly, DataGrid.ReadOnly,...).

Я попытался создать такой элемент управления, который по существу повторяет все дочерние элементы визуального дерева (рекурсивно) и соответственно обрабатывает элементы управления.

Это отлично работает, за исключением случая, когда дальнейшие изменения повлияют на визуальное дерево, чтобы добавить новых детей. Это верно для ContentControl или ItemsControl. Если дети добавляются в визуальное дерево после того, как я прошел через них, они явно не доступны для чтения.

У меня есть пчела, пытающаяся найти хорошее событие для реагирования (в основном обнаружить новых детей в визуальном дереве), но не смогла найти что-то подходящее. UpdateLayout уволен, но каждый раз часто просматривается визуальное дерево.

Есть ли способ решить это? Существует ли, вероятно, другой подход к тому, чтобы все (соответствующие) дети рекурсивно устанавливались только для чтения через привязку к родительскому элементу?

(И нет: я бы не захотел привязывать все свойства только для чтения к глобальному привязке вместо этого. Дело в том, чтобы иметь один элемент/часть, которая распространяется на всех дочерних элементов)

Ответы

Ответ 1

Вы можете сделать это с прикрепленным свойством, которое обеспечивает наследование значений:

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

Вы бы использовали его в XAML следующим образом:

<StackPanel local:ReadOnlyPanel.IsReadOnly="{Binding IsChecked, ElementName=cb}">
    <CheckBox x:Name="cb" Content="ReadOnly"/>
    <TextBox Text="Hello"/>
</StackPanel>