2016-10-06 2 views
1

Когда у меня есть элемент управления A, который содержит элемент управления B, есть свойства Prop, которые наследуются. Это означает, что B.Prop автоматически примет значение A.Prop, если B.Prop не установлено явно. Насколько я знаю, IsEnabled - такое свойство.Значение свойства, переопределяемое родителем, даже если установлено явно

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

В этой ситуации A является StackPanel и B текстовое поле:

<StackPanel> 
    <StackPanel.Style> 
     <Style TargetType="StackPanel"> 
      <Setter Property="IsEnabled" Value="True"/> 
      <Style.Triggers> 
       <DataTrigger Binding="{Binding InDisableMode}" Value="True"> 
        <Setter Property="IsEnabled" Value="False"/> 
       </DataTrigger> 
      </Style.Triggers> 
     </Style> 
    </StackPanel.Style> 
    <TextBox Text="some text"> 
     <TextBox.Style> 
      <Style TargetType="TextBox"> 
       <Setter Property="IsEnabled" Value="False"/> 
       <Style.Triggers> 
        <DataTrigger Binding="{Binding InDisableMode}" Value="True"> 
         <Setter Property="IsEnabled" Value="True"/> 
        </DataTrigger> 
       </Style.Triggers> 
      </Style> 
     </TextBox.Style> 
    </TextBox> 
</StackPanel> 

выше XAML фрагмент имеет свой DataContext установить на мой ViewModel. ViewModel содержит свойство InDisableMode, которое является bool. Когда это false, все будет как и ожидалось: StackPanel включен и TextBox отключен.

Но когда InDisableMode является true, оба StackPanel и TextBox отключены, хотя оба триггера должны запускаться!

Примечание: Я знаю, что я могу привязатьк InDisableMode в обоих элементах управления (в TextBox напрямую, в StackPanel с помощью конвертера дополнений). Я не пробовал, если это работает, так как я хочу сделать это с помощью триггеров.

EDIT:

Точка отключения StackPanel является отключение всех своих детей легко (кроме TextBox, который я хочу, чтобы включить вместо него). Любые другие идеи, как решить эту задачу без изменения отношений родитель-потомок или создания новых элементов управления? На данный момент единственный способ, который я вижу, - отключить всех детей, кроме TextBox, один за другим ...

ответ

1

Если вы отключили элемент управления, его дети отключены. Поскольку StackPanel не является интерактивным, нет причин отключать его, кроме как отключить его интерактивных детей.

Если вы хотите включить элемент управления A, пока его родитель B отключен, вы не можете этого сделать. B не может быть родителем, если вы хотите включить A, а B отключен.

Для решения проблемы, вы можете поместить их как в Grid, с TextBox определенной последним, накладываться TextBox на вершине StackPanel. Затем он будет находиться в пределах области StackPanel, но он не будет дочерним элементом StackPanel.

1

Это происходит потому, что свойство UIElement.IsEnabled использует принудительное значение стоимости, наследуя значение от его родителя. Это можно сделать, используя CoerceValueCallback. Значение coercion занимает первое место в рейтинге Dependency Property Setting Precedence List.

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

Итак, предположим, что мы хотим, чтобы наши Button вести себя по-другому, мы должны создать новый Button, как показано ниже:

public class CButton : Button 
{ 
    public static readonly DependencyProperty IsEnabled; 

    static CButton() 
    { 
     IsEnabled = UIElement.IsEnabledProperty.AddOwner(typeof(CButton), 
       new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.None, 
        UIElement.IsEnabledProperty.DefaultMetadata.PropertyChangedCallback, 
        new CoerceValueCallback(IsEnabledCoerceCallback))); 
    } 

    private static object IsEnabledCoerceCallback(DependencyObject d, object baseValue) 
    {    
     return (bool) baseValue; 
    } 
} 

Здесь мы возвращаемся присвоенное значение, как и от IsEnabledCoerceCallback. Перед возвратом вы также можете ввести поведение: если пользователь не предоставляет никакого значения для IsEnabled, то используйте унаследованное значение от родителя, иначе используйте CButton.IsEnabled пользовательское значение.

На боковой ноте попробуйте установить null вместо new CoerceValueCallback(IsEnabledCoerceCallback), посмотрите, что произойдет.

+0

«Это происходит потому, что свойство UIElement.IsEnabled использует принудительное значение ценности, наследуя значение от его родителя». - но если ребенок всегда наследует значение от своего родителя, почему возможно включить родительский элемент и отключить его? – Kjara

+1

@ Kjara Наследование и переписывание - это два случая различий. Принуждение означает, что вы хотите переписать поведение по умолчанию. Label.FontSize = 20 изменяет шрифты всех текстовых блоков внутри него, но если вы установите FontSize для определенного, это будет соблюдаться. – AnjumSKhan