WPF переопределяет IsEnabled от родителя

Я просто искал способ включить дочерний элемент управления, в то время как родительский элемент управления имеет IsEnabled = false. Все ответы, которые я нашел до сих пор, говорят, что это невозможно - нужно включить родителя и отключить дочерние элементы управления, кроме тех, которые все еще должны быть включены.

Однако, переопределив метаданные для IsEnabledProperty в файле App.xaml.cs, я смог изменить это поведение по умолчанию:

protected override void OnStartup(StartupEventArgs e)
{
    UIElement.IsEnabledProperty.OverrideMetadata(typeof(FrameworkElement),
             new UIPropertyMetadata(true,IsEnabledChanged, CoerceIsEnabled));
}

private void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var childrenCount = VisualTreeHelper.GetChildrenCount(d);
    for (int i = 0; i < childrenCount; ++i)
    {
        var child = VisualTreeHelper.GetChild(d, i);
        child.CoerceValue(UIElement.IsEnabledProperty);
    }
}
private object CoerceIsEnabled(DependencyObject d, object basevalue)
{
    var parent = VisualTreeHelper.GetParent(d) as FrameworkElement;
    if (parent != null && parent.IsEnabled == false)
    {
        if (d.ReadLocalValue(UIElement.IsEnabledProperty) == DependencyProperty.UnsetValue)
        {
            return false;
        }
    }
    return basevalue;
}

Теперь вы можете вручную установить свойство IsEnabled для дочернего элемента, которое переопределяет родительское значение.

Есть ли недостатки такого подхода?

Ответ 1

Это работало для моей ситуации над элементом управления, используемым несколько раз с некоторыми небольшими изменениями.

Размещение здесь, чтобы помочь любым будущим поисковым веб-сайтам в подобной ситуации:

  • поместил его в статический конструктор вместо события, иначе он попытался установить его несколько раз и бросил "PropertyMetadata уже зарегистрирован для типа" {type} ". исключение.
  • Изменен тип, соответствующий элементу управления

код:

Обязательно найдите и замените [CustomControl] типом имени вашего элемента управления.

static [CustomControl]()
{
    UIElement.IsEnabledProperty.OverrideMetadata(typeof([CustomControl]), new UIPropertyMetadata(true, [CustomControl]_IsEnabledChanged, CoerceIsEnabled));
}

private static void [CustomControl]_IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var childrenCount = VisualTreeHelper.GetChildrenCount(d);
    for (int i = 0; i < childrenCount; ++i)
    {
        var child = VisualTreeHelper.GetChild(d, i);
        child.CoerceValue(UIElement.IsEnabledProperty);
    }
}

private static object CoerceIsEnabled(DependencyObject d, object basevalue)
{
    var parent = VisualTreeHelper.GetParent(d) as FrameworkElement;
    if (parent != null && parent.IsEnabled == false)
    {
        if (d.ReadLocalValue(UIElement.IsEnabledProperty) == DependencyProperty.UnsetValue)
        {
            return false;
        }
    }
    return basevalue;
}

Ответ 2

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

Как явствует из комментариев, я бы сказал, что редизайн этого окна поможет. Особенно, если я хотел бы запретить только редактирование (модификацию данных) в форме, я бы использовал другие свойства, такие как IsReadOnly.

Ответ 3

Другой вариант - переопределить FrameworkPropertyMetadataOptions, чтобы удалить свойство Inherits. У меня была аналогичная проблема с FontSize, и это сработало хорошо:

FontSizeProperty.OverrideMetadata(
   typeof(YourControl), 
   new FrameworkPropertyMetadata(8.0, 
      FrameworkPropertyMetadataOptions.None, changed));