2016-04-27 6 views
2

Рассмотрим это (отредактированный вниз) Style, предназначенный для Button которого Content является String:TemplatedParent равно нулю, когда используется внутри DataTrigger ControlTemplate в

<Style x:Key="Test" TargetType="Button"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="Button"> 
       <StackPanel> 
        <TextBlock x:Name="text" Text="{TemplateBinding Content}" /> 
        <TextBlock x:Name="demo" Text="{Binding RelativeSource={RelativeSource TemplatedParent}}" /> 
       </StackPanel> 
       <ControlTemplate.Triggers> 
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"> 
         <DataTrigger.Value> 
          <system:String>Test</system:String> 
         </DataTrigger.Value> 
         <Setter TargetName="test" Property="Foreground" Value="Red" /> 
        </DataTrigger> 
       </ControlTemplate.Triggers> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

Намерение в данном примере, чтобы включить кнопку текст красным если он равен слову «Тест» . Но это не сработает, потому что привязка триггера TemplatedParent разрешает null, а не Button, к которому применяется Style. Тем не менее, TextBlock с именем «demo» будет иметь свой Text, установленный в «System.Windows.Controls.Button: [ButtonText]», как ожидалось, что означает, что TemplatedParent работает правильно на этом уровне. Почему он не работает внутри DataTrigger?


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

+0

Помогите, если вы измените 1. DataTrigger -> Триггер и 2. TemplatedParent -> Self в триггере? – ASh

+0

@ASh Да, это действительно работает, но для моего фактического случая мне нужно использовать 'DataTrigger', потому что я действительно работаю с non-DependencyProperty TemplatedParent. – dlf

ответ

2

TemplatedParent в вашем ControlTemplate.Triggers не то, что вы ожидаете. Внутри триггера он фактически ссылается на Button.TemplatedParent. Таким образом, он будет только не нулевым, если вы создадите эту кнопку внутри шаблона. Вы не создаете кнопку внутри шаблона, поэтому в вашем случае это значение равно null. Теперь рассмотрим этот XAML:

<Window.Resources> 
    <Style x:Key="Test" 
      TargetType="Button"> 
     <Setter Property="Template"> 
      <Setter.Value> 
       <ControlTemplate TargetType="Button"> 
        <StackPanel> 
         <TextBlock x:Name="text" 
            Text="dummy" /> 
         <TextBlock x:Name="demo" 
            Text="{Binding RelativeSource={RelativeSource TemplatedParent}}" /> 
        </StackPanel> 
        <ControlTemplate.Triggers> 
         <DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"> 
          <DataTrigger.Value> 
           <system:String>Test</system:String> 
          </DataTrigger.Value> 
          <Setter TargetName="text" 
            Property="Foreground" 
            Value="Red" /> 
         </DataTrigger> 
        </ControlTemplate.Triggers> 
       </ControlTemplate> 
      </Setter.Value> 
     </Setter> 
    </Style> 
    <Style x:Key="Test2" TargetType="ContentControl"> 
     <Setter Property="Template"> 
      <Setter.Value> 
       <ControlTemplate TargetType="ContentControl"> 
        <Button Style="{StaticResource Test}"></Button> 
       </ControlTemplate> 
      </Setter.Value> 
     </Setter> 
    </Style> 
</Window.Resources> 
<Grid> 
    <!--<Button Content="Test" Style="{StaticResource Test}"/>--> 
    <ContentControl Style="{StaticResource Test2}" Content="Test" /> 
</Grid> 

Здесь я retemplate ContentControl и внутри шаблона Я использую кнопку с шаблоном. Если вы запустите этот код, вы увидите «фиктивный» текст в красном цвете, потому что Button.TemplatedParent теперь ContentControl, и у него есть Content равно «Тест», что подтверждает сказанное выше.

Теперь вернемся к вашей проблеме: просто изменить RelativeSource TemplatedParent к RelativeSource Self (нет необходимости менять DataTrigger к Trigger) - это один будет ссылаться на вашу кнопку.

+0

Вы явно правы. Означает ли это, что триггер «привязан» к самой кнопке, даже если он определен в Шаблоне Button? Есть ли какой-то принцип в игре, который заставил бы меня ожидать, если бы я это знал? – dlf

+1

Вы можете так думать: все, что находится внутри ControlTemplate (StackPanel и т. Д.), Является дочерним элементом этого шаблона. Всякий раз, когда вы создаете новую кнопку с помощью ControlTemplate, все эти дети будут создаваться каждый раз, основываясь на шаблоне.Триггеры не являются «дочерними» шаблонами, они являются частью определения шаблона, а не «созданы из шаблона» каждый раз при создании новой кнопки. Вы также можете различать визуальные элементы управления (те дети в шаблоне) и невизуальные (триггеры). – Evk

0

Я не совсем уверен, но я думаю, что триггер равен referenc, потому что Content возвращает объект. Таким образом, это никогда не будет верно с вашей строкой, определенной в триггере.

+0

Довольно уверенно, что 'String' реализует' IEquatable ', WPF будет использовать' String.Equals() 'для сравнения. Но в любом случае подход работает отлично, если я изменяю DataTrigger на обычный триггер. Кроме того, используя [QuickConverter] (https://quickconverter.codeplex.com/), я могу подтвердить, что привязка TemplatedParent оценивается как null в этом (но только в этом) случае. – dlf

+0

Ну, это имеет смысл. Но у меня возникла аналогичная проблема, когда я использовал DataTemplates. Я не использую DataTriggers для элементов управления в Temlpate. Это произошло потому, что триггеры находятся в отдельном пространстве имен (логическое дерево), чем сам шаблон. Возможно, это та же причина. –