【问题标题】:WPF - Is there a way to target an element type in a ControlTemplate's trigger?WPF - 有没有办法在 ControlTemplate 的触发器中定位元素类型?
【发布时间】:2015-03-18 00:17:34
【问题描述】:

我定义了以下 ControlTemplate:

<ControlTemplate x:Key="buttonTemplate" TargetType="{x:Type Button}">
    <Border x:Name="buttonBorder">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <TextBlock x:Name="txtLabel" Grid.Column="0">
                <ContentPresenter/>
            </TextBlock>
            <Canvas x:Name="reschedule" Grid.Column="1">
                <Path x:Name="path1" ... />
                <Path x:Name="path2" ... />
                <Path x:Name="path3" ... />
                <Path x:Name="path4" ... />
                <Path x:Name="path5" ... />
                <Path x:Name="path6" ... />
                <Path x:Name="path7" ... />
                <Path x:Name="path8" ... />
                <Path x:Name="path9" ... />
                <Path x:Name="path10" ... />
            </Canvas>
        </Grid>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter TargetName="buttonBorder" Property="Background" Value="DarkGreen"/>
        </Trigger>
        <Trigger Property="IsEnabled" Value="False">
            <Setter TargetName="buttonBorder" Property="Background" Value="DarkGray"/>
            <Setter TargetName="txtLabel" Property="Foreground" Value="Gray"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

目前默认的 TextBlock ForegroundPath Fill 属性已设置为 White。当按钮被禁用时,我想将这些属性设置为Gray。现在它适用于TextBlock,我也可以通过定位每个名称来使其适用于Paths,但是有没有办法按类型定位所有Path元素?比如:

<Setter TargetType="Path" Property="Fill" Value="Gray"/>

我尝试将以下触发器添加到 Border 元素的样式中,但它不起作用:

<Border.Style>
    <Style TargetType="Border">
        <Style.Resources>
            <Style TargetType="Path">
                <Style.Triggers>
                    <Trigger Property="IsEnabled" Value="False">
                        <Setter Property="Fill" Value="Gray"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Style.Resources>
    </Style>
</Border.Style>

【问题讨论】:

  • 是的,在您的风格中使用TargetType="{x:Type Path}",这应该针对Path 类型的所有元素。 HTH
  • 看起来没有在样式块中定义的触发器没有TargetType属性
  • 在问题中你有这个 xaml &lt;Border.Style&gt;&lt;Style TargetType="Border"&gt;&lt;Style.Resources&gt; 这是你放置你的 TargetType="{x:Type Path}" 的地方。所以不要使用&lt;Style TargetType="Path",而是使用TargetType="{x:Type Path}"。顺便说一句,将此样式放在模板定义之外,否则将不会对其进行评估。 HTH
  • 太棒了,谢谢!我以为TargetType="Path"TargetType="{x:Type Path}" 是相同的......?无论哪种方式,它似乎都有效。起作用的是我必须将我的样式从模板定义中取出,然后按键引用该样式。
  • 如果你愿意我可以创建一个答案并向你解释TargetType="Path"TargetType="{x:Type Path}"之间的区别?

标签: wpf xaml triggers controltemplate targettype


【解决方案1】:

您的样式不起作用的原因是它位于模板内部。
要让它工作,您需要在模板之外应用您的样式。不知道为什么这样做,可能与 xaml 中样式的处理方式有关。
另外要讨论的是样式定义的意义:
&lt;Style TargetType="Path"&gt;不会和&lt;Style TargetType="{x:Type Path}"&gt;一样。
如果在资源标签中定义,第一个会给你一个错误,因为它需要一个键,你可以通过它在所有目标类型中显式引用样式。
后者被分配给Path 类型的每个控件,所以如果你在DockPanel 中定义它,那么DockPanel 内部的每个Path 都会受到样式的影响,但是如果PathDockPanel 不会应用任何样式,除非在别处明确定义,否则此样式将隐式应用于控件。
高温

【讨论】:

  • hm... 在查看了为什么TargetType="Path"TargetType="{x:Type Path}" 不同的其他一些解释之后,似乎除了RelativeSource 绑定和BasedOn="{StaticResource {x:Type Button}}" 之类的情况之外通常没有区别。请参阅 herehere
【解决方案2】:

1) 使用Canvas Resource存储路径样式。

请参阅 &lt;Trigger Property="IsEnabled" Value="False"&gt;&lt;Trigger Property="IsEnabled" Value="True"&gt;

 <Window.Resources>
    <ControlTemplate x:Key="buttonTemplate" TargetType="{x:Type Button}">
        <Border x:Name="buttonBorder">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <TextBlock x:Name="txtLabel" Grid.Column="0">
            <ContentPresenter/>
                </TextBlock>                                       
                <Canvas x:Name="reschedule" Grid.Column="1">
                    <Path x:Name="path1" Data="M 0 0 L 0 10 L 10 10 Z"/>
                    <Path x:Name="path2"  Data="M 0 0 L 0 10 L 10 10 Z" />                     
                </Canvas>
            </Grid>
        </Border>
        <ControlTemplate.Triggers>                                            
            <Trigger Property="IsEnabled" Value="False">
                <Setter TargetName="buttonBorder" Property="Background" Value="DarkGray"/>
                <Setter TargetName="reschedule" Property="Style">
                    <Setter.Value>
                        <Style TargetType="{x:Type Canvas}">
                            <Style.Resources>
                                <Style TargetType="{x:Type Path}">
                                    <Setter Property="Fill" Value="Green"></Setter>
                                </Style>
                            </Style.Resources>
                        </Style>
                    </Setter.Value>
                </Setter>
            </Trigger>                                       
            <Trigger Property="IsEnabled" Value="True">                 
                <Setter TargetName="reschedule" Property="Style">
                    <Setter.Value>
                        <Style TargetType="{x:Type Canvas}">
                            <Style.Resources>
                                <Style TargetType="{x:Type Path}">
                                    <Setter Property="Fill" Value="Blue"></Setter>
                                </Style>
                            </Style.Resources>
                        </Style>
                    </Setter.Value>
                </Setter>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>       
</Window.Resources>

<StackPanel>
    <Button Height="40" Width="40" Template="{StaticResource buttonTemplate}" IsEnabled="False"></Button>
    <Button Height="40" Width="40" Margin="10" Template="{StaticResource buttonTemplate}" IsEnabled="True"></Button>
</StackPanel>

2) 使用 Canvas 标记

  <Window.Resources>
    <ControlTemplate x:Key="buttonTemplate" TargetType="{x:Type Button}">
        <Border x:Name="buttonBorder">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <TextBlock x:Name="txtLabel" Grid.Column="0">
            <ContentPresenter/>
                </TextBlock>
                <Canvas x:Name="reschedule" Tag="Red" Grid.Column="1">
                    <Path x:Name="path1" Fill="{Binding Path=Tag,RelativeSource={RelativeSource AncestorType={x:Type Canvas}}}" Data="M 0 0 L 0 10 L 10 10 Z"/>
                    <Path x:Name="path2" Fill="{Binding Path=Tag,RelativeSource={RelativeSource AncestorType={x:Type Canvas}}}"  Data="M 0 0 L 0 10 L 10 10 Z" />
                </Canvas>
            </Grid>
        </Border>
        <ControlTemplate.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter TargetName="buttonBorder" Property="Background" Value="DarkGreen"/>
            </Trigger>
            <Trigger Property="IsEnabled" Value="False">
                <Setter TargetName="buttonBorder" Property="Background" Value="DarkGray"/>
                <Setter TargetName="reschedule" Property="Tag" Value="Green"/>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>
</Window.Resources>

<StackPanel>
    <Button Height="40" Width="40" Template="{StaticResource buttonTemplate}" IsEnabled="False"></Button>
    <Button Height="40" Width="40" Margin="10" Template="{StaticResource buttonTemplate}" IsEnabled="True"></Button>
</StackPanel>

【讨论】:

    【解决方案3】:

    你可以试试这个技巧:

    Binding创建代理控件:

    <Control x:Name="Proxy" Background="White" /> 
    

    并像这样在Path绑定中使用:

    <Path x:Name="path1" Fill="{Binding Path=Background, ElementName=Proxy}" Data="..." />
    

    当您在触发器中设置代理的颜色时,他会隐藏所有路径。

    或者代替绑定代理,您可以使用任何现有的控件,例如TextBlock

    完整示例:

    <ControlTemplate x:Key="buttonTemplate" TargetType="{x:Type Button}">
        <Border x:Name="buttonBorder">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
    
                <TextBlock x:Name="txtLabel" Grid.Column="0">                        
                    <ContentPresenter />
                </TextBlock>
    
                <Control x:Name="Proxy" Background="White" /> 
    
                <Canvas x:Name="reschedule" Grid.Column="1">
                    <Path x:Name="path1" Fill="{Binding Path=Background, ElementName=Proxy}" Data="..." />
                    <Path x:Name="path2" Fill="{Binding Path=Background, ElementName=Proxy}" Data="..." />
                </Canvas>
            </Grid>
        </Border>
    
        <ControlTemplate.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter TargetName="buttonBorder" Property="Background" Value="DarkGreen" />
            </Trigger>
    
            <Trigger Property="IsEnabled" Value="False">
                <Setter TargetName="buttonBorder" Property="Background" Value="DarkGray" />
                <Setter TargetName="txtLabel" Property="Foreground" Value="Gray" />
                <Setter TargetName="Proxy" Property="Background" Value="Gray" />
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>
    

    【讨论】:

    • 我最终选择了这个解决方案,但我没有创建代理控件,而是将Path Fill 属性设置为Fill="{Binding Path=Foreground, ElementName=txtLabel}",因为TextBlock 已经具有与代理相同的行为
    猜你喜欢
    • 1970-01-01
    • 2022-01-05
    • 2012-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-26
    • 1970-01-01
    • 2010-10-31
    相关资源
    最近更新 更多