WPF Setter 目标绑定到按钮内容的名称

WPF Setter Target binding to button content's name

我构建了一个 ControlTemplate,用于 window 中的所有按钮,但有一个需要额外的触发器,当触发时会更改其内容值。 显然我不想将此触发器添加到控件模板上,因为它会影响使用同一控件模板的所有其他按钮。所以我想做的是将数据触发器添加到按钮的特定样式上,然后引用内容名称并直接在我认为很容易的样式中更改它。

这是我目前得到的:

<Button HorizontalAlignment="Right" 
        VerticalAlignment="Top" 
        Width="34"
        BorderThickness="0"
        Height="30"
        Padding="0"
        Click="OnMaximiseClick">
     <Button.Style>
         <Style TargetType="Button" BasedOn="{StaticResource WindowButtonStyle}">
             <Style.Triggers>
                 <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=WindowState}" Value="Maximised">
                     <Setter TargetName="MaxPath"
                             Property="Data"
                             Value="F1M0,10L0,3 3,3 3,0 10,0 10,2 4,2 4,3 7,3 7,6 6,6 6,5 1,5 1,10z M1,10L7,10 7,7 10,7 10,2 9,2 9,6 6,6 6,9 1,9z" />
                 </DataTrigger>
             </Style.Triggers>
         </Style>
     </Button.Style>

     <Path x:Name="MaxPath"
           Width="10"
           Height="10"
           Data="F1M0,0L0,9 9,9 9,0 0,0 0,3 8,3 8,8 1,8 1,3z"
           SnapsToDevicePixels="True"
           Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" />
</Button>

问题是Setter无法访问TargetName="MaxPath",我也试过{Binding ElementName=MaxPath},但是卡住了。我知道我可以在按钮样式中再次构建 ControlTemplate 但它有许多我不想在两个地方维护的触发器,我确信必须有一种方法可以快速轻松地完成此操作但我就是看不到它。

是否有在 Style 中引用按钮内容的方法,或者我是否必须在 Buttons ControlTemplate 中重建 Path 然后重新定义我所有的 Triggers 在这个新的 ControlTemplate?

您可能需要一个 ControlTemplate:

<Button 
     ... [your other properties here] >

        <Button.Template>
            <ControlTemplate>
                <Path x:Name="MaxPath"
                    Width="10"
                   Height="10"
                   Data="F1M0,0L0,9 9,9 9,0 0,0 0,3 8,3 8,8 1,8 1,3z"
                   SnapsToDevicePixels="True"
                   Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" />
                <ControlTemplate.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=WindowState}" Value="Minimised">
                        <Setter TargetName="MaxPath"
                         Property="Data"
                         Value="F1M0,10L0,3 3,3 3,0 10,0 10,2 4,2 4,3 7,3 7,6 6,6 6,5 1,5 1,10z M1,10L7,10 7,7 10,7 10,2 9,2 9,6 6,6 6,9 1,9z" />
                    </DataTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Button.Template>
    </Button>
</Grid>

如果需要,您可以添加其他触发器..

转念一想你不需要 DataTrigger。只需将 2 Path 直接绑定到 window 状态即可。不过,您将需要可重复使用的转换器。

下面是一个演示(使按钮可点击并添加绑定到 Fill):

xaml:

<Button>
    <Grid Width="10"
          Height="10">
        <Path Fill="Black"
              Data="F1M0,0L0,9 9,9 9,0 0,0 0,3 8,3 8,8 1,8 1,3z"
              Visibility="{Binding WindowState, RelativeSource=RelativeSource FindAncestor, AncestorType=Window}, ConverterParameter=Invert, Converter={local:StateToVisibilityConverter}}" />
        <Path Fill="Black"
              Data="F1M0,10L0,3 3,3 3,0 10,0 10,2 4,2 4,3 7,3 7,6 6,6 6,5 1,5 1,10z M1,10L7,10 7,7 10,7 10,2 9,2 9,6 6,6 6,9 1,9z"
              Visibility="{Binding WindowState, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Converter={local:StateToVisibilityConverter}}" />
    </Grid>
</Button>

转换器:

public class StateToVisibilityConverter : MarkupExtension, IValueConverter
{
    public StateToVisibilityConverter() { }

    public override object ProvideValue(IServiceProvider serviceProvider) => this;

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var result = (WindowState)value == WindowState.Maximized;
        if ((string)parameter == "Invert")
            result = !result;
        return result ? Visibility.Visible : Visibility.Hidden;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); }
}

第三次思考(仅xaml):

<Button>
    <Path Fill="Black"
          Width="10"
          Height="10">
        <Path.Style>
            <Style TargetType="Path">
                <Style.Setters>
                    <Setter Property="Data"
                            Value="F1M0,10L0,3 3,3 3,0 10,0 10,2 4,2 4,3 7,3 7,6 6,6 6,5 1,5 1,10z M1,10L7,10 7,7 10,7 10,2 9,2 9,6 6,6 6,9 1,9z" />
                </Style.Setters>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding WindowState, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
                                 Value="Maximized">
                        <Setter Property="Data"
                                Value="F1M0,0L0,9 9,9 9,0 0,0 0,3 8,3 8,8 1,1 1,3z" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Path.Style>
    </Path>
</Button>