1

我正在尝试编写一个显示背景渐变的 Button 控件,从自定义属性指定的颜色开始,以硬编码的颜色结束。在我看来,这应该是一个简单的控制模板,但我无法让它工作。

具有Color依赖属性的子类“按钮”:

public class GradientButton : Button
{
    public Color BackgroundColor
    {
        get { return (Color)GetValue(BackgroundColorProperty); }
        set { SetValue(BackgroundColorProperty, value); }
    }

    public static readonly DependencyProperty BackgroundColorProperty =
        DependencyProperty.Register("BackgroundColor", typeof(Color), typeof(GradientButton), new PropertyMetadata((sender, args) => {
            System.Diagnostics.Debug.WriteLine("Set bg col");
        }));

    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(GradientButton), null);
    public GradientButton()
        : base()
    {
        this.DefaultStyleKey = typeof(GradientButton);
    }
}

控制模板,使用BackgroundColorDP 设置背景渐变:

<Style TargetType="custom:GradientButton">
    <Setter Property="HorizontalAlignment" Value="Left" />
    <Setter Property="VerticalAlignment" Value="Top" />
    <Setter Property="Width" Value="200" />

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="custom:GradientButton">
                <Grid>
                    <Grid.Background>
                        <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                            <GradientStop Color="{TemplateBinding BackgroundColor}" Offset="0" />
                            <GradientStop Color="Gray" Offset="1" />
                        </LinearGradientBrush>
                    </Grid.Background>

                    <TextBlock Text="{TemplateBinding Text}" />
                    <TextBlock Text="{TemplateBinding BackgroundColor}" HorizontalAlignment="Right" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

如果我使用这样的控件:

<custom:GradientButton Text="Foo" BackgroundColor="#FF0000" /> 

然后我希望看到一个带有红色到灰色背景渐变的按钮。相反,只有渐变的灰色部分出现:

控件不显示背景的红色部分

我错过了什么令人痛苦的显而易见的事情?


编辑强调:

  1. 我添加了“文本”DP 来证明我的 DP / TemplateBinding 实际上是犹太洁食。
  2. 请注意,我无法将“BackgroundColor”显示为文本块中的文本——但我已经在调试器中确认它实际上是在控件上设置的
4

3 回答 3

1

没问题,

你的第一个问题是你如何声明你的渐变。您当前的方式将为上半部分显示一条红色实线(或指定的任何颜色),为下半部分显示一条灰色实线。你想要这样的东西,它会为你做剩下的事情。

<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
     <GradientStop Color="Red" Offset="0"/>
     <GradientStop Color="Gray" Offset="1"/>
</LinearGradientBrush>

此外,您可以让自己更轻松,只需利用已经存在的属性来实现类似的目的,这样您就不需要任何额外的代码。就像Tag属性一样(我个人根据实例将各种不同的废话塞进不同的目的。所以这样的东西也足够了,不需要额外的依赖声明(除非它真的很重要,称为“背景颜色”) ;

<Style TargetType="custom:GradientButton">
    <Setter Property="HorizontalAlignment" Value="Left" />
    <Setter Property="VerticalAlignment" Value="Top" />
    <Setter Property="Width" Value="200" />
    <Setter Property="Tag" Value="Red"/><!-- For the sake of having a default -->

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="custom:GradientButton">
                <Grid>
                    <Grid.Background>
                        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                             <GradientStop Color="{TemplateBinding Tag}" Offset="0"/>
                             <GradientStop Color="Gray" Offset="1"/>
                        </LinearGradientBrush>
                    </Grid.Background>

                    <ContentPresenter />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

这应该对你有好处,希望这会有所帮助。:)

于 2013-07-24T21:02:59.037 回答
1

很抱歉造成混淆。我的回答不正确。这里的问题是 TemplateBinding。TemplateBinding 是编译时的轻量级绑定,而使用 TemplatedParent 发生在运行时,因此 TemplateBinding 有一些限制,其中之一是它不能绑定到 Freezables。您可以做的是使用传统的模板父代:

<GradientStop Color="{Binding Path=BackgroundColor, RelativeSource={RelativeSource TemplatedParent}}" Offset="0" />

可以参考http://blogs.msdn.com/b/liviuc/archive/2009/12/14/wpf-templatebinding-vs-relativesource-templatedparent.aspx

“你会认为编译器或运行时会抛出某种错误,但显然不会。”

一个好点。我在输出窗口中也没有看到任何绑定错误。但是,我使用 Snoop 来查看 Color 属性,发现正是我所期望的:

System.Windows.Data 错误:2:找不到目标元素的管理 FrameworkElement 或 FrameworkContentElement。绑定表达式:路径=背景颜色;数据项=空;目标元素是 'GradientStop' (HashCode=4605357); 目标属性是“颜色”(类型“颜色”)

于 2013-07-24T21:23:16.133 回答
1

我发现了一种解决方法:如果TemplateBinding我使用RelativeSource绑定而不是 ,那么我的按钮会按照您的预期呈现:

Color="{Binding RelativeSource={RelativeSource AncestorType=custom:GradientButton},Path=BackgroundColor}"

当然,这毫无意义。我确信这是一个错误(或者至少是人类不应该知道的那些事情之一)。

于 2013-07-24T21:29:10.507 回答