【问题标题】:Control that uses "Color" type dependency property使用“颜色”类型依赖属性的控件
【发布时间】:2013-07-24 13:11:41
【问题描述】:

我正在尝试编写一个显示背景渐变的 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);
    }
}

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

<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 实际上是 kosher。
  2. 请注意,我无法将“BackgroundColor”显示为文本块中的文本,但我已在调试器中确认它实际上是在控件上设置的

【问题讨论】:

  • 您有四个 GradientStop 属性而没有 GradientStart 属性?只是猜测-我一点也不熟悉。

标签: c# silverlight xaml dependency-properties controltemplate


【解决方案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);目标属性是“颜色”(输入“颜色”)

【讨论】:

  • 抱歉 -- 我的意思是,GradientStop 是一个 DependencyObject,所以它应该可以在绑定中使用,不是吗?
  • 你完全正确。我编辑了我的答案。抱歉回答错误!
  • 啊,这可以解释......所以我不能使用 TemplateBinding 来定位 Color 属性,因为它是一个 Freezable (它在 Silverlight 中不存在,但有些类似,我猜)。你会认为编译器或运行时会抛出某种错误,但显然不会。无论如何,谢谢你的解释。
【解决方案2】:

没问题,

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

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

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

【讨论】:

  • 会不会是BackgroundColor 而不是Tag
  • @McAden 如果他想使用他编写的所有附加代码来指定特定的自定义依赖项属性,那就是这样。否则Tag 已经存在,你可以通过它传递你想要的任何东西,除了他的模板之外不需要任何东西。
  • 我认为这就是他想要的,否则为什么要使用TargetType="custom:GradientButton"
  • 我想整体使用不仅仅是制作自定义按钮模板,但那是 OP 的业务。对于他需要的任何东西,这只是一条更简单的路线,至少对于他的这一点要求而言,不需要任何代码……除非它绝对必须具有特定的依赖属性来代替仅使用现有的框架元素已经在他的手中完成同样的效果。只是想成为有帮助的朋友。 ;)
  • 我尝试了您的更改,但它们不会影响任何内容。 Color 类型似乎发生了一些非常奇怪的事情 - 请参阅我对问题的编辑以及我发布的“答案”。
【解决方案3】:

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

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

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

【讨论】:

  • 嗨莎!只要你得到你的补救措施。到目前为止,AncestorType 在 SL 低于 5 的所有方面对我来说都是一个痛苦的 a$$,所以绝对是 +1
  • @ChrisW。哈,谢谢。尽管如此,我还是不同意任何人向我解释这一点......实际上我可能会开始赏金。
  • 难道不只是指定一个直接路径来在它需要的数据上下文中找到您的属性吗?否则,伙计,我很久以前就不再质疑这些小事了……哈哈在某些情况下不值得努力。
猜你喜欢
  • 2011-05-25
  • 2017-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多