Binding a resource to a custom control property

2020-04-17 07:01发布

I am creating a custom button that shows slightly faded text normally, and full-strength text on a MouseOver or MouseDown. I have defined two resources in the Generic.xaml of my control to represent the brushes for these text colors:

<!-- Text Brushes -->
<SolidColorBrush x:Key="NormalTextBrush" Color="Black" />
<SolidColorBrush x:Key="FadedTextBrush" Color="Gray" />

The control compiles and works fine in that configuration.

But I want to let the control user set the text color, using the custom control's Foreground property. So, I changed the resource declarations to this:

<!-- Text Brushes -->
<SolidColorBrush x:Key="NormalTextBrush" Color="{Binding Path=Foreground, RelativeSource={RelativeSource TemplatedParent}}" />
<SolidColorBrush x:Key="FadedTextBrush" Color="{Binding Path=Foreground, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource ColorConverter}, ConverterParameter='1.2'}" />

The second declaration uses an HSL value converter to fade the text color.

Now the control doesn't work, and I get the following error in the output window:

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=Foreground; DataItem='TaskButton' (Name='Button1'); target element is 'SolidColorBrush' (HashCode=38118303); target property is 'Color' (type 'Color')
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=Foreground; DataItem=null; target element is 'SolidColorBrush' (HashCode=47449297); target property is 'Color' (type 'Color')

I'm not sure what the Data Error is telling me. Can anyone tell me what's going on and how to fix it? Thanks for your help.

2条回答
一纸荒年 Trace。
2楼-- · 2020-04-17 07:18

The problem is that you cannot use RelativeSource bindings on elements defined in resources, because they are not a part of visual or logical tree.

To fix this you just need to set these binding in places where you set the references to your resources (in the control template of your button). Something like this:

<ControlTemplate TargetType="{x:Type Button}">
   <Border x:Name="brd" 
           TextBlock.Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource TemplatedParent}}">
      ...
   </Border>
   <ControlTemplate.Triggers>
      <Trigger Property="IsMouseOver"
               Value="True">
          <Setter TargetName="brd"
                  Property="TextBlock.Foreground"
                  Value="{Binding Path=Foreground, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource ColorConverter}, ConverterParameter='1.2'}"/>
      </Trigger>
   </ControlTemplate.Triggers>
</ControlTemplate>

In other words, you don't need to define resourses - NormalTextBrush and FadedTextBrush.

查看更多
forever°为你锁心
3楼-- · 2020-04-17 07:20

RelativeSource TemplatedParent only (IIRC) has meaning within a control template, and it refers to a property on the instance of the control on which the template is applied.

A UserControl's content is not the template of the UserControl. So this binding won't consider the parent UserControl as a viable target.

The error message refers to the fact that a SolidColorBrush does not have a template; it does not extend System.Windows.Controls.Control, which is the base type of (most) all templated UI controls. See Control.Template for more information about templating.

What you want to do is set a relative source of FindAncestor.

{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}

This will walk up the visual (or is it logical?) tree to find the first ancestor of type UserControl, then bind against a public property called Foreground.

However this will NOT work if the SolidColorBrush is defined as a Resource. Resources are not part of the visual (or logical tree, or both? still not clear) and therefore a RelativeSource binding will not be able to walk the tree's ancestry.

You will have to use the binding directly on whatever control you wish to have the same foreground color as the UserControl.

查看更多
登录 后发表回答