使用附加属性进行模板绑定

30

我有一个标准样式用于我的按钮,但我希望样式的某些部分可以配置。例如,当触发鼠标悬停在按钮上时,我希望边框出现,并且我想可配置边框颜色。

根据这篇文章:http://www.thomaslevesque.com/2011/10/01/wpf-creating-parameterized-styles-with-attached-properties/,我认为我可以使用附加属性和TemplateBinding来实现这一点。

我创建了以下附加属性:

public static class ThemeProperties
{
    public static Brush GetButtonBorderColour(DependencyObject obj)
    {
        return (Brush)obj.GetValue(ButtonBorderColourProperty);
    }

    public static void SetButtonBorderColour(DependencyObject obj, Brush value)
    {
        obj.SetValue(ButtonBorderColourProperty, value);
    }

    public static readonly DependencyProperty ButtonBorderColourProperty =
        DependencyProperty.RegisterAttached(
            "ButtonBorderColour",
            typeof(Brush),
            typeof(ThemeProperties),
            new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.Inherits));
}

我这样设置属性:

<Button Style="{StaticResource RedButton}" local:ThemeProperties.ButtonBorderColour="#B20000"/>

我的样式看起来像这样:

<Window.Resources>
    <Style x:Key="RedButton" TargetType="Button">
        <Setter Property="OverridesDefaultStyle" Value="True"/>
        <Setter Property="Margin" Value="2"/>
        <Setter Property="FontFamily" Value="Tahoma"/>
        <Setter Property="FontSize" Value="11px"/>
        <Setter Property="FontWeight" Value="Bold"/>
        <Setter Property="Foreground" Value="White"/>
        <Setter Property="MinHeight" Value="25" />

        <Setter Property="FocusVisualStyle" Value="{StaticResource MyFocusVisual}" />
        <Setter Property="Background" >
            <Setter.Value>
                <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
                    <GradientStop Color="#FECCBF" Offset="0.2"/>
                    <GradientStop Color="Red" Offset="0.85"/>
                    <GradientStop Color="#FECCBF" Offset="1"/>
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border Name="border" BorderThickness="1" Padding="4,2" BorderBrush="Transparent" CornerRadius="3" Background="{TemplateBinding Background}">
                        <Grid >
                            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Name="content"/>
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="border" Property="BorderBrush" Value="{TemplateBinding local:ThemeProperties.ButtonBorderColour}" />
                            <Setter Property="Foreground" Value="#B20000" />
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter Property="Background" >
                                <Setter.Value>
                                    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
                                        <GradientStop Color="#FECCBF" Offset="0.35"/>
                                        <GradientStop Color="Red" Offset="0.95"/>
                                        <GradientStop Color="#FECCBF" Offset="1"/>
                                    </LinearGradientBrush>
                                </Setter.Value>
                            </Setter>
                            <Setter TargetName="content" Property="RenderTransform" >
                                <Setter.Value>
                                    <TranslateTransform Y="1.0" />
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                        <Trigger Property="IsDefaulted" Value="True">
                            <Setter TargetName="border" Property="BorderBrush" Value="#B20000" />
                        </Trigger>
                        <Trigger Property="IsFocused" Value="True">
                            <Setter TargetName="border" Property="BorderBrush" Value="#B20000" />
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="border" Property="Opacity" Value="0.7" />
                            <Setter Property="Foreground" Value="Gray" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

关键行是:

<Trigger Property="IsMouseOver" Value="True">
    <Setter TargetName="border" Property="BorderBrush" Value="{TemplateBinding local:ThemeProperties.ButtonBorderColour}" />
    <Setter Property="Foreground" Value="#B20000" />
</Trigger>

据我所知,这段代码应该能够正常工作,但是在上面这一行运行时出现了以下错误:

无法将属性“Value”中的值转换为类型为“”的对象。 在标记文件中的对象“System.Windows.Setter”处发生错误。

我做错了什么?我刚开始接触WPF,但无法弄清楚问题所在,因为附加属性的类型是Brush,而这正是我期望Border的BorderBrush属性需要的。

2个回答

60

我认为TemplateBinding是在编译时计算的,因此您无法动态设置Setter中的TemplateBinding,请尝试改用Binding代替(请参见下文)。

<Setter TargetName="border" Property="BorderBrush" 
        Value="{Binding Path=(local:ThemeProperties.ButtonBorderColour),
                        RelativeSource={RelativeSource TemplatedParent}}"/>

希望这可以帮到你。


嗨,我编辑了我的答案,现在应该可以工作了。这是一个语法错误,对此我很抱歉。 - XiaoChuan Yu
谢谢小川,这完美地解决了我的问题。我也能看到我的代码和链接网页上的示例之间的区别,因为示例没有在setter中使用TemplateBinding,这是我之前没有意识到的。再次感谢! - thudbutt
我不明白为什么我的相同情况在设计时不起作用(但在运行时却可以)- http://stackoverflow.com/questions/41614676/binding-style-property-value-to-an-attached-property-causes-design-time-only-e/41617979 - hyankov
1
你可以使用 Value={TemplateBinding local:ThemeProperties.ButtonBorderColour}。请注意,绑定表达式周围缺少括号。 - Leon Bohmann

-3

试试这个:

<Setter TargetName="border" Property="BorderBrush" Value="{TemplateBinding Path=(local:ThemeProperties.ButtonBorderColour)}" />

区别在于括号表示该属性是一个附加属性。


不幸的是,它似乎没有被识别为有效的语法,它只返回一个类型错误,指出“(local:ThemeProperties”未找到。 - thudbutt
2
抱歉,您需要在其前面加上 Path=。这是 XAML 解析器的一个怪癖。我会更新我的答案。 - Kent Boogaart
仍然没有成功,仍然被标记为无效语法,并显示错误信息:“在'TemplateBindingExtension'类型中未找到属性'Path'”。 - thudbutt
1
根据小川的建议,将TemplateBinding更改为Binding。 - Kent Boogaart
感谢您的帮助,肯特。当与绑定而不是模板绑定结合使用时,括号是有效的语法(遵循您使用小川的解决方案的建议)。 - thudbutt
2
这个答案无效,它仍然建议使用TemplateBinding。我建议更新它。 - ANeves

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接