How to avoid error using binding in resource?
I'm now working on my first wpf poject. I put a Style
in Windows.Resource
to reference in following controls.
<Window.Resources>
<Style x:Key="EmojiButtonStyle" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Padding" Value="1" />
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect
BlurRadius="4"
Opacity="1.0"
ShadowDepth="0"
Color="Gray" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border
x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="true">
<ContentPresenter
x:Name="contentPresenter"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Focusable="False"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true" />
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="TextBlock.Effect">
<Setter.Value>
<DropShadowEffect
BlurRadius="5"
ShadowDepth="0"
Color="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}, Path=Foreground, Mode=OneWay, Converter={StaticResource SingleValueDebugPrintConverter}}" />
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="OrangeRed" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="TextBlock.Effect">
<Setter.Value>
<DropShadowEffect
BlurRadius="2"
ShadowDepth="0"
Color="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}, Path=Foreground, Mode=OneWay, Converter={StaticResource SingleValueDebugPrintConverter}}" />
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="DarkRed" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="contentPresenter" Property="TextElement.Foreground" Value="LightGray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
It works well, however I got an error when application startup.
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=Foreground; DataItem=null; target element is 'DropShadowEffect' (HashCode=65300541); target property is 'Color' (type 'Color')
I'm sure the error is due to Color="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}, Path=Foreground, Mode=OneWay, Converter={StaticResource SingleValueDebugPrintConverter}}"
, that binding in Windows.Resource
has no ancestor untill being referenced by other control.
Am I doing this in the right way? Is the error can be avoided?
Update #1
Have been looking into there similar questions for hours, none of them matches what I came cross.
Binding targets need to be part of the visual tree. There's no data context in the resource dictionary. The exact fix will depend on your scenario specifics. See duplicate for generalized advice. – Peter Duniho
Peter Duniho 's answer may explained why binding
in Window.Resource
is not working, but I can not find any walkaround. ProxyElement
trick mentioned in other post is aim to binding to DataContext
not Ancestor
.
Update #2
Anyhow, I'v found the right way to do so. Use TemplateBinding
instead of FindAncestor
.
<Window.Resources>
<Style x:Key="EmojiButtonStyle" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Padding" Value="1" />
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect
BlurRadius="4"
Opacity="1.0"
ShadowDepth="0"
Color="Gray" />
</Setter.Value>
</Setter>
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border
x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="true">
<ContentPresenter
x:Name="contentPresenter"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Focusable="False"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="TextBlock.Effect">
<Setter.Value>
<DropShadowEffect
BlurRadius="8"
ShadowDepth="0"
Color="{TemplateBinding Property=Foreground}" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="TextBlock.Effect">
<Setter.Value>
<DropShadowEffect
BlurRadius="3"
ShadowDepth="0"
Color="{TemplateBinding Property=Foreground}" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="contentPresenter" Property="TextElement.Foreground" Value="LightGray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
Thanks to Styles and templates (WPF .NET). Stackoverflow really needs to review someone's question before rush it as duplicate
.