Custom Control Styling
-
I have created a Hyperlink control. What I want is to have a default style in my control itself, but allow consumers to restyle it later, like when It's dropped into a window. So here's my control's XAML:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Marois.Framework.Core.WPF.Controls"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"><SolidColorBrush x:Key="hyperlinkMouseOverBrush" Color="Green"/> <Style TargetType="{x:Type local:MaroisHyperlink}"> <Setter Property="FontSize" Value="60" /> <Setter Property="Hyperlink.TextDecorations" Value="None" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <TextBlock> <Hyperlink> <TextBlock Text="{Binding LinkText, RelativeSource={RelativeSource TemplatedParent}}" FontSize="{TemplateBinding FontSize}"/> </Hyperlink> </TextBlock> </ControlTemplate> </Setter.Value> </Setter> <Style.Triggers> <Trigger Property="IsMouseOver" Value="true"> <Setter Property="Hyperlink.Foreground" Value="{StaticResource hyperlinkMouseOverBrush}" /> <Setter Property="Hyperlink.TextDecorations" Value="Underline" /> </Trigger> <Trigger Property="IsEnabled" Value="false"> <Setter Property="Hyperlink.Foreground" Value="Gray" /> <Setter Property="Hyperlink.TextDecorations" Value="None" /> </Trigger> </Style.Triggers> </Style>
</ResourceDictionary>
Code behind:
using System.Windows;
using System.Windows.Media;namespace Marois.Framework.Core.WPF.Controls
{
public class MaroisHyperlink : ControlBase
{
#region DP's
#region DP HoverBrush
public static readonly DependencyProperty HoverBrushP -
I have created a Hyperlink control. What I want is to have a default style in my control itself, but allow consumers to restyle it later, like when It's dropped into a window. So here's my control's XAML:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Marois.Framework.Core.WPF.Controls"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"><SolidColorBrush x:Key="hyperlinkMouseOverBrush" Color="Green"/> <Style TargetType="{x:Type local:MaroisHyperlink}"> <Setter Property="FontSize" Value="60" /> <Setter Property="Hyperlink.TextDecorations" Value="None" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <TextBlock> <Hyperlink> <TextBlock Text="{Binding LinkText, RelativeSource={RelativeSource TemplatedParent}}" FontSize="{TemplateBinding FontSize}"/> </Hyperlink> </TextBlock> </ControlTemplate> </Setter.Value> </Setter> <Style.Triggers> <Trigger Property="IsMouseOver" Value="true"> <Setter Property="Hyperlink.Foreground" Value="{StaticResource hyperlinkMouseOverBrush}" /> <Setter Property="Hyperlink.TextDecorations" Value="Underline" /> </Trigger> <Trigger Property="IsEnabled" Value="false"> <Setter Property="Hyperlink.Foreground" Value="Gray" /> <Setter Property="Hyperlink.TextDecorations" Value="None" /> </Trigger> </Style.Triggers> </Style>
</ResourceDictionary>
Code behind:
using System.Windows;
using System.Windows.Media;namespace Marois.Framework.Core.WPF.Controls
{
public class MaroisHyperlink : ControlBase
{
#region DP's
#region DP HoverBrush
public static readonly DependencyProperty HoverBrushPKevin Marois wrote:
The Hyperlink.TextDecorations does not.
Firstly, it's
Inline.TextDecorations
, notHyperlink.TextDecorations
. Inline.TextDecorations Property (System.Windows.Documents) | Microsoft Learn[^] Setting that property on a parent element doesn't affect theHyperlink
, since it overrides the property in its default template. You either need to set the property explicitly on theHyperlink
:or via a style:
<Setter Property="TextDecorations" Value="None" />
If you want to be able to override it when you use your control, you'll need a property to control it:
public class MaroisHyperlink : ControlBase
{
public static readonly DependencyProperty TextDecorationsProperty = Inline.TextDecorationsProperty.AddOwner(typeof(MaroisHyperlink));public TextDecorationCollection TextDecorations { get { return (TextDecorationCollection)GetValue(TextDecorationsProperty); } set { SetValue(TextDecorationsProperty, value); } }
and then bind the property in your template:
Kevin Marois wrote:
None of the triggers at the bottom work.
A similar problem. The triggers should set the properties of your control, and the relevant properties of the
Hyperlink
should be bound to the properties on your control. -
Kevin Marois wrote:
The Hyperlink.TextDecorations does not.
Firstly, it's
Inline.TextDecorations
, notHyperlink.TextDecorations
. Inline.TextDecorations Property (System.Windows.Documents) | Microsoft Learn[^] Setting that property on a parent element doesn't affect theHyperlink
, since it overrides the property in its default template. You either need to set the property explicitly on theHyperlink
:or via a style:
<Setter Property="TextDecorations" Value="None" />
If you want to be able to override it when you use your control, you'll need a property to control it:
public class MaroisHyperlink : ControlBase
{
public static readonly DependencyProperty TextDecorationsProperty = Inline.TextDecorationsProperty.AddOwner(typeof(MaroisHyperlink));public TextDecorationCollection TextDecorations { get { return (TextDecorationCollection)GetValue(TextDecorationsProperty); } set { SetValue(TextDecorationsProperty, value); } }
and then bind the property in your template:
Kevin Marois wrote:
None of the triggers at the bottom work.
A similar problem. The triggers should set the properties of your control, and the relevant properties of the
Hyperlink
should be bound to the properties on your control.Thanks. I learned some new things here. One question - That DP syntax you used is unfamiliar to me. How do you set the default value for the TextDecoration's DP? I've always used this syntax for a DP, but for some reason it won't compile here:
public static readonly DependencyProperty TextDecorationsProperty =
DependencyProperty.Register("TextDecorations",
typeof(TextDecorations),
typeof(MaroisHyperlink),
new PropertyMetadata(TextDecorations.Underline));public TextDecorations TextDecorations
{
get { return (TextDecorations)GetValue(TextDecorationsProperty);}
set { SetValue(TextDecorationsProperty, value); }
}I get 3 compilation errors:
Cannot convert to static type 'TextDecorations'
'TextDecorations': static types cannot be used as return types
'TextDecorations': static types cannot be used as paramtersIf it's not broken, fix it until it is. Everything makes sense in someone's mind. Ya can't fix stupid.
-
Kevin Marois wrote:
The Hyperlink.TextDecorations does not.
Firstly, it's
Inline.TextDecorations
, notHyperlink.TextDecorations
. Inline.TextDecorations Property (System.Windows.Documents) | Microsoft Learn[^] Setting that property on a parent element doesn't affect theHyperlink
, since it overrides the property in its default template. You either need to set the property explicitly on theHyperlink
:or via a style:
<Setter Property="TextDecorations" Value="None" />
If you want to be able to override it when you use your control, you'll need a property to control it:
public class MaroisHyperlink : ControlBase
{
public static readonly DependencyProperty TextDecorationsProperty = Inline.TextDecorationsProperty.AddOwner(typeof(MaroisHyperlink));public TextDecorationCollection TextDecorations { get { return (TextDecorationCollection)GetValue(TextDecorationsProperty); } set { SetValue(TextDecorationsProperty, value); } }
and then bind the property in your template:
Kevin Marois wrote:
None of the triggers at the bottom work.
A similar problem. The triggers should set the properties of your control, and the relevant properties of the
Hyperlink
should be bound to the properties on your control.I stumbled across this also. I needed put the triggers INSIDE the template:
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true"> <Setter TargetName="hyperLink" Property="TextDecorations" Value="{Binding TextDecorations, RelativeSource={RelativeSource TemplatedParent}}"/> <Setter TargetName="hyperLink" Property="Foreground" Value="{Binding HoverBrush, RelativeSource={RelativeSource TemplatedParent}}"/> </Trigger>
</ControlTemplate.Triggers>
If it's not broken, fix it until it is. Everything makes sense in someone's mind. Ya can't fix stupid.
-
Kevin Marois wrote:
The Hyperlink.TextDecorations does not.
Firstly, it's
Inline.TextDecorations
, notHyperlink.TextDecorations
. Inline.TextDecorations Property (System.Windows.Documents) | Microsoft Learn[^] Setting that property on a parent element doesn't affect theHyperlink
, since it overrides the property in its default template. You either need to set the property explicitly on theHyperlink
:or via a style:
<Setter Property="TextDecorations" Value="None" />
If you want to be able to override it when you use your control, you'll need a property to control it:
public class MaroisHyperlink : ControlBase
{
public static readonly DependencyProperty TextDecorationsProperty = Inline.TextDecorationsProperty.AddOwner(typeof(MaroisHyperlink));public TextDecorationCollection TextDecorations { get { return (TextDecorationCollection)GetValue(TextDecorationsProperty); } set { SetValue(TextDecorationsProperty, value); } }
and then bind the property in your template:
Kevin Marois wrote:
None of the triggers at the bottom work.
A similar problem. The triggers should set the properties of your control, and the relevant properties of the
Hyperlink
should be bound to the properties on your control.I beleive I finally have it working! This was mainly about me learning how to create Custome Controls. I appreciate all your help. I learned a lot. Here's the finiished product! Generic.xaml
<Style TargetType="{x:Type local:MaroisHyperlink}">
<Setter Property="Template"> <Setter.Value> <ControlTemplate> <TextBlock> <Hyperlink x:Name="hyperLink" Foreground="{Binding Foreground, RelativeSource={RelativeSource TemplatedParent}}" TextDecorations="{Binding TextDecorations, RelativeSource={RelativeSource TemplatedParent}}"> <TextBlock Text="{Binding LinkText, RelativeSource={RelativeSource TemplatedParent}}"/> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Control}, Path=LinkClickedCommand}"/> </i:EventTrigger> </i:Interaction.Triggers> </Hyperlink> </TextBlock> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="true"> <Setter TargetName="hyperLink" Property="TextDecorations" Value="{Binding TextDecorations, RelativeSource={RelativeSource TemplatedParent}}"/> <Setter TargetName="hyperLink" Property="Foreground" Value="{Binding HoverBrush, RelativeSource={RelativeSource TemplatedParent}}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter>
</Style>
Code Behind
public class MaroisHyperlink : ControlBase { #region Routed Events #region LinkClickedEvent public static readonly RoutedEvent LinkClickedEvent = EventManager.RegisterRoutedEvent("LinkClicked", RoutingStrategy.Bubble, typ
-
Thanks. I learned some new things here. One question - That DP syntax you used is unfamiliar to me. How do you set the default value for the TextDecoration's DP? I've always used this syntax for a DP, but for some reason it won't compile here:
public static readonly DependencyProperty TextDecorationsProperty =
DependencyProperty.Register("TextDecorations",
typeof(TextDecorations),
typeof(MaroisHyperlink),
new PropertyMetadata(TextDecorations.Underline));public TextDecorations TextDecorations
{
get { return (TextDecorations)GetValue(TextDecorationsProperty);}
set { SetValue(TextDecorationsProperty, value); }
}I get 3 compilation errors:
Cannot convert to static type 'TextDecorations'
'TextDecorations': static types cannot be used as return types
'TextDecorations': static types cannot be used as paramtersIf it's not broken, fix it until it is. Everything makes sense in someone's mind. Ya can't fix stupid.
The
AddOwner
syntax lets you register another type for use with aDependencyProperty
which has already been registered: How to: Add an Owner Type for a Dependency Property - WPF .NET Framework | Microsoft Learn[^] DependencyProperty.AddOwner Method (System.Windows) | Microsoft Learn[^] You can't use theTextDecorations
class[^] as the property type; that's astatic
class used to provide access to the standard text decorations. Instead, you need to use theTextDecorationCollection
class[^] as the property type, which is what theInlines.TextDecorations
property[^] does:/// /// DependencyProperty for