I'm try to set the Foreground
colour on a Hyperlink
using a Style
object in an ancestor's Resources
, but it's not having any effect. I even used the BasedOn
tip from Changing Hyperlink foreground without losing hover color, but it's not made any difference - I still get a blue hyperlink that is red on hover.
Here's the XAML for my controls, including an ItemsControl
whose items are shown using a hyperlink:
<StackPanel Background="Red" TextElement.Foreground="White">
<StackPanel.Resources>
<Style TargetType="Hyperlink" BasedOn="{StaticResource {x:Type Hyperlink}}">
<Setter Property="Foreground" Value="Yellow"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<TextBlock>Data import errors</TextBlock>
<ItemsControl ItemsSource="{Binding Errors}"/>
</StackPanel>
And the items in the ItemsControl
are picking up the following DataTemplate
:
<DataTemplate DataType="{x:Type Importer:ConversionDetailsMessage}">
<TextBlock>
<Run Text="{Binding Message, Mode=OneTime}"/>
<Hyperlink Command="Common:ImportDataCommands.ExplainConversionMessage" CommandParameter="{Binding}">
<Run Text="{Binding HelpLink.Item2, Mode=OneTime}"/>
</Hyperlink>
</TextBlock>
</DataTemplate>
It's worth noting, too, that I don't want to just set the different colours directly on the Hyperlink
in the DataTemplate
. This is because the template will be used by a number of different ItemsControl
objects, most of which will be on a white background and so can use the standard hyperlink colours. (Note that the one in the XAML above, however, has a red background.)
In short, I don't want the DataTemplate
to have to know anything about the control in which it's being used. The styles for the template's controls should just filter down to it.
So... can anyone tell me why the style's not filtering down and what I can do to fix it?
Thanks.
Update:
Since I couldn't get Pavlo's answer to work in my production app, I've since tried it in a separate test app. The app is a WinForms app, with the main form containing nothing but an ElementHost
, which itself contains a simple WPF usercontrol. Here's its XAML:
<UserControl x:Class="DataTemplateStyling.StylingView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:DataTemplateStyling="clr-namespace:DataTemplateStyling"
x:Name="root" Loaded="StylingViewLoaded">
<UserControl.Resources>
<Style x:Key="MyDefaultHyperlinkStyle" BasedOn="{StaticResource {x:Type Hyperlink}}"/>
<DataTemplate DataType="{x:Type DataTemplateStyling:ImportMessage}">
<DataTemplate.Resources>
<Style TargetType="{x:Type Hyperlink}"
BasedOn="{StaticResource MyDefaultHyperlinkStyle}"/>
</DataTemplate.Resources>
<TextBlock>
<Run Text="{Binding Message, Mode=OneTime}"/>
<Hyperlink NavigateUri="{Binding HelpLink.Item1}">
<Run Text="{Binding HelpLink.Item2, Mode=OneTime}"/>
</Hyperlink>
</TextBlock>
</DataTemplate>
</UserControl.Resources>
<Grid DataContext="{Binding ElementName=root}">
<StackPanel Background="Red" TextElement.Foreground="White">
<StackPanel.Resources>
<Style x:Key="MyDefaultHyperlinkStyle" TargetType="Hyperlink" BasedOn="{StaticResource {x:Type Hyperlink}}">
<Setter Property="Foreground" Value="Yellow"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<TextBlock>Data import errors</TextBlock>
<ItemsControl ItemsSource="{Binding Messages}"/>
</StackPanel>
</Grid>
</UserControl>
As it stands above, this generates an InvalidOperationException
, stating "Can only base on a Style with target type that is base type 'IFrameworkInputElement'."
That can be fixed by putting TargetType="Hyperlink"
on the Style
definition immediately inside the UserControl.Resources
element. However, while the messages are being shown, the link part of them still has the default blue hyperlink styling:
In short, it's not working, so I'd welcome any other suggestions/corrections. :(
Update 2:
Thanks to an alternative solution from Pavlo, it now is working. :)
After some googling I ran into this post: http://www.11011.net/archives/000692.html.
As it described there, it turns out that elements that are not derived from
Control
(likeTextBlock
andHyperlink
) do not look for implicit styles outside theDataTemplate
boundary.Again, as the article says, the possible workaround is to specify the style key explicitly. In your case it might be something like this:
Then, you can add an implicit style for
Hyperlink
that just references our named style in theDataTemplate
resources:And because the data template can be used in different places, there is a possibility that parent container doesn't define a style with key "MyDefaultHyperlinkStyle". In this case an exception will be thrown saying that resource "MyDefaultHyperlinkStyle" cannot be found. To address this, you can define a style with such key that will only inherit default style somewhere in App.xaml:
Update:
The code you included in your update will not work because of the nature of static resources, which means that the following resource reference in date template...
... will always point to the following resource (which is the default style):
Static resource references are resolved at compile time, therefore the closest resource in the tree is used.
You might be tempted to use
DynamicResource
, but unfortunately it is not supported with theBasedOn
property.BUT,
Foreground
property supports dynamic resources, so we can use the same trick with brushes we use inside our style. Here is your test user control modified to use dynamic brushes:It works as expected, i.e. all links inside
StackPanel
will be Yellow/White, while outside they will be blue.Hope this helps.