Reputation: 19132
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.
Upvotes: 2
Views: 3417
Reputation: 20756
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
.
Upvotes: 2
Reputation:
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
.
Upvotes: 4