2016-04-27 1 views
2

Betrachten Sie diese (edited-down) Style, entworfen für eine Button deren Content ist ein String:TemplatedParent ist null, wenn sie innerhalb eines Datatrigger des Control verwendet

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

Die Absicht ist in diesem Beispiel auf den Button Text röten wenn es gleich dem Wort "Test" ist. Aber es funktioniert nicht, weil die TemplatedParent Bindung des Auslösers auf null aufgelöst wird, anstatt auf Button, das auf Style angewendet wird. Die TextBlock namens "demo" wird jedoch wie erwartet auf "System.Windows.Controls.Button: [ButtonText]" festgelegt, dh TemplatedParent funktioniert ordnungsgemäß auf dieser Ebene. Warum funktioniert es nicht innerhalb der DataTrigger? Ich weiß, es gibt andere Möglichkeiten, das zu erreichen, aber ich versuche zu verstehen, warum die Bindung nicht so funktioniert, wie ich es erwarte.

+0

hilft es, wenn Sie 1. DataTrigger ändern -> Trigger und 2. TemplatedParent -> Selbst im Trigger? – ASh

+0

@ASh Ja, das funktioniert, aber für meinen tatsächlichen Fall muss ich einen 'DataTrigger' verwenden, weil ich wirklich mit einer Nicht-DependencyProperty des TemplatedParent arbeite. – dlf

Antwort

2

TemplatedParent in Ihrem ControlTemplate.Triggers ist nicht das, was Sie erwarten. Innerhalb des Triggers verweist er tatsächlich auf Button.TemplatedParent. Als solches wird es nur nicht null sein, wenn Sie diese Schaltfläche innerhalb der Vorlage erstellen. Sie erstellen keine Schaltfläche in der Vorlage, daher ist sie in Ihrem Fall ungültig. Betrachten wir nun diese 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> 

Here I retemplate ContentControl und innen Vorlage verwende ich Taste mit Ihrer Vorlage. Wenn Sie diesen Code ausführen, sehen Sie "dummy" Text in rot, weil Button.TemplatedParent jetzt ContentControl ist, und es hat Content gleich "Test", was bestätigt, was ich oben sagte.

jetzt zum Problem zurück: nur RelativeSource TemplatedParent zu RelativeSource Self ändern (keine Notwendigkeit DataTrigger zu Trigger ändern) - dies Ihrem Knopf verweisen würde.

+0

Sie haben eindeutig Recht. Bedeutet dies, dass der Trigger auf den Button selbst "beschränkt" ist, obwohl er in der Vorlage des Buttons definiert ist? Gibt es hier einen Grundsatz, der mich dazu gebracht hätte zu erwarten, dass wenn ich es gewusst hätte? – dlf

+1

Sie können es sich so vorstellen: Was auch immer in ControlTemplate (StackPanel etc) ist, sind Kinder dieser Vorlage. Jedes Mal, wenn Sie mit Ihrer ControlTemplate eine neue Schaltfläche erstellen, werden alle diese untergeordneten Elemente jedes Mal basierend auf der Vorlage erstellt.Trigger sind keine "Kinder" der Vorlage, sie sind Teil der Schablonendefinition und nicht "aus der Vorlage erstellt", wenn Sie eine neue Schaltfläche erstellen. Sie können auch zwischen visuellen Steuerelementen (diese untergeordneten Elemente in der Vorlage) und nicht visuellen (Triggern) unterscheiden. – Evk

0

Ich bin mir nicht ganz sicher, aber ich denke, der Trigger ist gleichbedeutend mit referenc, weil Inhalt ein Objekt zurückgibt. Es wird also niemals wahr sein, wenn Ihre Zeichenkette im Trigger definiert ist.

+0

Ziemlich sicher, da 'String'' IEquatable 'implementiert, wird WPF' 'String.Equals()' für den Vergleich verwenden. Aber in jedem Fall funktioniert der Ansatz gut, wenn ich den DataTrigger zu einem gewöhnlichen Trigger ändere. Durch die Verwendung von [QuickConverter] (https://quickconverter.codeplex.com/) kann ich außerdem bestätigen, dass die TemplatedParent-Bindung in diesem (aber nur diesem) Fall auf null auswertet. – dlf

+0

Nun, das macht Sinn. Aber ich habe ein ähnliches Problem, wenn ich DataTemplates verwendet. Ich konnte DataTriggers nicht in Controls innerhalb des Templates verwenden. Das passierte, weil die Trigger sich in einem separaten Namespace (Logical Tree) befinden als das Template selbst. Vielleicht ist das der gleiche Grund hier. –

Verwandte Themen