Попытка наследовать тему / стиль и применение дополнительных триггеров

#wpf #xaml #styles #themes #derived

#wpf #xaml #стили #темы #производный

Вопрос:

Я пытаюсь работать и понимать иерархию XAML для стилей… в простом, простом текстовом поле … повсюду видно, как установить «отключенный» цвет фона на основе флага «IsEnabled». Отлично, понял.

Теперь я хочу иметь другой класс, производный от TextBox… MyTextBox. Для этого класса у меня есть свойство (не свойство зависимости, поэтому я использовал DataTrigger). Итак, я хочу сохранить все обычные действия с текстовым полем, которые работали, но теперь получите новый триггер для правильного обновления цвета фона на какой-либо другой цвет.. Итак, вот что у меня есть. просто чтобы уточнить, все мои статические ресурсы для цветов — СПЛОШНЫЕ КИСТИ…

 <Style TargetType="TextBox" >
  <Setter Property="FontFamily" Value="Courier New" />
  <Setter Property="FontSize" Value="12" />
  <Setter Property="Foreground" Value="{StaticResource MyForeground}" />
  <Setter Property="Background" Value="{StaticResource MyBackground}" />
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="TextBox">
        <Border Name="Bd" BorderThickness="{TemplateBinding BorderThickness}" 
            BorderBrush="{TemplateBinding BorderBrush}"
            Background="{TemplateBinding Background}" 
            SnapsToDevicePixels="true">

            <ScrollViewer Name="PART_ContentHost" 
                Background="{TemplateBinding Background}" 
                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
        </Border>

        <ControlTemplate.Triggers>
          <Trigger Property="IsEnabled" Value="False">
            <Setter Property="Background" Value="{StaticResource MyDisBackground}" />
            <Setter TargetName="PART_ContentHost" Property="Background" 
                 Value="MyDisBackground"/>
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

<!--  Now, my derived (or so I was hoping) style that just adds additional trigger -->
<Style TargetType="local:MyTextBox"  BasedOn="{StaticResource {x:Type TextBox}}" >
  <Style.Triggers>
    <DataTrigger Binding="{Binding Path=IsRequired}" Value="True">
      <Setter Property="Background" Value="{StaticResource RequiredBackground}" />
    </DataTrigger>
  </Style.Triggers>
</Style>
  

Я упускаю что-то простое?

Ответ №1:

Во-первых, вы DataTrigger смотрите на DataContext свой MyTextBox (а не на сам элемент управления). Итак, посмотрите на элемент управления, вам нужно будет сделать что-то вроде:

 <DataTrigger Binding="{Binding Path=IsRequired, RelativeSource={RelativeSource Self}}" Value="True">
    <Setter Property="Background" Value="{StaticResource RequiredBackground}" />
</DataTrigger>
  

Теперь это установит MyTextBox.Background свойство, когда MyTextBox.IsRequired оно равно true. Но значения свойств зависимостей имеют порядок приоритета. Таким образом, вышеизложенное визуально изменит используемый фон, например:

 <local:MyTextBox />
  

В следующем случае ваша RequiredBackground кисть использоваться не будет. Вместо этого вы увидите MyDisBackground кисть:

 <local:MyTextBox IsEnabled="False" />
  

В этом случае значение ScrollViewer.Background изменяется на MyDisBackground и больше не привязывается к MyTextBox.Background свойству. Все MyTextBox.Background равно будет RequiredBackground , но он больше нигде не используется.

Наконец, в следующем случае ваша RequiredBackground кисть использоваться не будет.

 <local:MyTextBox Background="Yellow" />
  

Здесь локальное значение (желтое) находится на # 3 в списке приоритетов, в то время как параметр установки стиля находится на # 8.

Если вы создадите для своего свойства свойство зависимости, значение которого по умолчанию равно false, вы можете сделать что-то вроде:

 <Style TargetType="TextBox" >
  <Setter Property="FontFamily" Value="Courier New" />
  <Setter Property="FontSize" Value="12" />
  <Setter Property="Foreground" Value="{StaticResource MyForeground}" />
  <Setter Property="Background" Value="{StaticResource MyBackground}" />
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="TextBox">
        <Border Name="Bd" BorderThickness="{TemplateBinding BorderThickness}" 
            BorderBrush="{TemplateBinding BorderBrush}"
            Background="{TemplateBinding Background}" 
            SnapsToDevicePixels="true">

            <ScrollViewer Name="PART_ContentHost" 
                Background="{TemplateBinding Background}" 
                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
        </Border>

        <ControlTemplate.Triggers>
          <Trigger Property="local:MyTextBox.IsRequired" Value="False">
            <Setter Property="Background" Value="{StaticResource RequiredBackground}" />
            <Setter TargetName="PART_ContentHost" Property="Background" 
                 Value="RequiredBackground"/>
          </Trigger>
          <Trigger Property="IsEnabled" Value="False">
            <Setter Property="Background" Value="{StaticResource MyDisBackground}" />
            <Setter TargetName="PART_ContentHost" Property="Background" 
                 Value="MyDisBackground"/>
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

<Style TargetType="local:MyTextBox"  BasedOn="{StaticResource {x:Type TextBox}}" />
  

Несмотря на то, что свойство не существует для текстового поля, оно все равно может получить значение по умолчанию для вашего свойства зависимости и активировать его. Но поскольку он будет установлен для текстового поля, этот триггер никогда не будет использоваться.

Комментарии:

1. Что ж, я скопировал ваш код и внес первое предложенное изменение, в котором в CodeNaked указано Binding=»{Путь привязки = IsRequired, RelativeSource={RelativeSource Self}}» Это изменение само по себе, похоже, сработало (при условии, что ваш класс ‘MyTextBox’ реализует INotifyPropertyChanged с вашим свойством IsRequired . Я бы опубликовал свой код, чтобы показать вам, что именно я сделал… но я не хочу публиковать конкурирующий ответ, поскольку я не был тем, кто действительно его придумал.

2. @Scott, спасибо за оба ваших ввода, похоже, они работают так, как я надеялся, и посмотрите, как в ОБОИХ условиях… один как DependencyProperty, другой через INotifyPropertyChanged (который у меня уже был)

3. @DRapp, рад видеть, что ваше решение работает! Из двух решений, которые у вас есть… Я определенно предпочитаю решение свойств зависимостей, поскольку затем вы можете устанавливать значения непосредственно в XAML. Я только что заметил, что вы конкретно указали в своем вопросе, что вы не используете DependencyProperty и поэтому предположили, что вы предпочитаете избегать этого.