1

I want to add a hyperlink to my UWP App which looked like this

<Grid
  Background="Gray">
   <TextBlock>
       <Hyperlink
            Foreground="Red"
            NavigateUri="https://randomWebsiteLink">Click here</Hyperlink>
        </TextBlock>
  </Grid>

This works fine in light mode but when I change theme to a dark, hovering over the hyperlink, the "click here" becomes invisible as it blends with the grid background. I tried changing the color while hovering but i came across this post Changing Hover color of Hyperlink which recommends against doing this. I am now trying to create a custom hyperlink using a textbox

<Grid
  Background="Gray">
  <TextBlock
       Text="Click here"
       TextDecorations="Underline"
       Foreground="Gray">
           <interactivity:Interaction.Behaviors>
                <core:EventTriggerBehavior EventName="Tapped">
                        <core:InvokeCommandAction  Command="{x:Bind ViewModel.OnLinkClicked}"/>
                                </core:EventTriggerBehavior>
                        </interactivity:Interaction.Behaviors>
   </TextBlock>
  </Grid>

This works when I click on the text "Click here" I do see the link open in the browser. However, i would like to emulate the hyperlink control completely by manipulating visual states. I would like the foreground color of the text to change on hover or while pressed. I am thinking of using visual states. Is this a good idea and how do I go about this?

1 Answer 1

1

If you want to change the color of the text when hover or pressed, you need to create a custom style of your TextBox.

First, click the Document Outline window in the Visual Studio and find the target TextBox in the visual tree. Then right-click the TextBox and move your mouse to the selection - Edit Template. There will be a popup and you could choose Edit a Copy. After that, you will see a default TextBox style is added to the XAML resource.

Now you get the default style and the next step is to change the default behavior of the TextBox. There are many visual state groups in the default style, make sure that you are changing the correct one. The first visual state groups belong to the delete button of the TextBox, skip it. Go to the second visual state group, find the VisualState named PointerOver, you will see an animation that targets ContentElement's foreground property. This is the place you are looking for, change the value to the color you want to use.

The code looks like this:

        <Style x:Key="TextBoxStyle1" TargetType="TextBox">
        <Setter Property="Foreground" Value="{ThemeResource TextControlForeground}"/>
        <Setter Property="Background" Value="{ThemeResource TextControlBackground}"/>
            ..... some properties
            <Setter.Value>
                <ControlTemplate TargetType="TextBox">
                    <Grid>
                        <Grid.Resources>
                            <Style x:Name="DeleteButtonStyle" TargetType="Button">
                                ...some styles...
                        </Grid.Resources>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="Disabled">
                                ...some animations...
                            </VisualState>
                                <VisualState x:Name="PointerOver">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderBrush">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBorderBrushPointerOver}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="Background">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBackgroundPointerOver}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{Binding PlaceholderForeground, RelativeSource={RelativeSource Mode=TemplatedParent}, TargetNullValue={ThemeResource TextControlPlaceholderForegroundPointerOver}}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Red"/>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Focused">
                                 ...some animations...
                                </VisualState>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="ButtonStates">
                                <VisualState x:Name="ButtonVisible">
                                ...some animations...
                                </VisualState>
                                <VisualState x:Name="ButtonCollapsed"/>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <ContentPresenter x:Name="HeaderContentPresenter" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.ColumnSpan="2" Grid.Column="0" FontWeight="Normal" Foreground="{ThemeResource TextControlHeaderForeground}" Margin="{ThemeResource TextBoxTopHeaderMargin}" Grid.Row="0" TextWrapping="Wrap" VerticalAlignment="Top" Visibility="Collapsed" x:DeferLoadStrategy="Lazy"/>
                        <Border x:Name="BorderElement" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="{TemplateBinding CornerRadius}" Grid.ColumnSpan="2" Grid.Column="0" Control.IsTemplateFocusTarget="True" MinHeight="{ThemeResource TextControlThemeMinHeight}" MinWidth="{ThemeResource TextControlThemeMinWidth}" Grid.RowSpan="1" Grid.Row="1"/>
                        <ScrollViewer x:Name="ContentElement" AutomationProperties.AccessibilityView="Raw" Grid.Column="0" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}" IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}" IsTabStop="False" IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}" Margin="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Grid.Row="1" VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" ZoomMode="Disabled"/>
                        <TextBlock x:Name="PlaceholderTextContentPresenter" Grid.ColumnSpan="2" Grid.Column="0" Foreground="{Binding PlaceholderForeground, RelativeSource={RelativeSource Mode=TemplatedParent}, TargetNullValue={ThemeResource TextControlPlaceholderForeground}}" IsHitTestVisible="False" Margin="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Grid.Row="1" Text="{TemplateBinding PlaceholderText}" TextWrapping="{TemplateBinding TextWrapping}" TextAlignment="{TemplateBinding TextAlignment}"/>
                        <Button x:Name="DeleteButton" AutomationProperties.AccessibilityView="Raw" BorderThickness="{TemplateBinding BorderThickness}" Grid.Column="1" FontSize="{TemplateBinding FontSize}" IsTabStop="False" MinWidth="34" Margin="{ThemeResource HelperButtonThemePadding}" Grid.Row="1" Style="{StaticResource DeleteButtonStyle}" VerticalAlignment="Stretch" Visibility="Collapsed"/>
                        <ContentPresenter x:Name="DescriptionPresenter" AutomationProperties.AccessibilityView="Raw" Content="{TemplateBinding Description}" Grid.ColumnSpan="2" Grid.Column="0" Foreground="{ThemeResource SystemControlDescriptionTextForegroundBrush}" Grid.Row="2" x:Load="False"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

For more information, please refer to the document: Tutorial: Create custom styles

Update:

If you are using TextBlock instead of using TextBox now, then you could try another simple way.

You could try to follow the code:

In your Xaml:

<Grid>
    <TextBlock x:Name="MyTextBlock" Foreground="Aqua" Height="100" Text="This is a test Text" PointerEntered="MyTextBlock_PointerEntered" PointerExited="MyTextBlock_PointerExited" />
    
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="TextBlockStates">
            <VisualState x:Name="Nor"/>
            <VisualState x:Name="PointOver">
                <Storyboard>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="MyTextBlock" Storyboard.TargetProperty="Foreground">
                        <DiscreteObjectKeyFrame KeyTime="0" Value="Red"/>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Grid>

In the code behind:

 private void MyTextBlock_PointerExited(object sender, PointerRoutedEventArgs e)
    {
        VisualStateManager.GoToState(this, "Nor", true);
    }

    private void MyTextBlock_PointerEntered(object sender, PointerRoutedEventArgs e)
    {
        
        VisualStateManager.GoToState(this, "PointOver", true);
    }
Sign up to request clarification or add additional context in comments.

10 Comments

Li : Thank you for this. I tried editing a template but i get only an option to create an empty style.I also get an error st Setter.Value saying that Value is not recognized or is not asleep.Is there a difference between TextBlock and TextBox
Are you using TextBlock? If you are using TextBlock, then you won't be able to create a default style.
Another way to get a default style of TextBox is to search it in the generic.xaml file. You could easily find a ThemeResource on your XAML page, then select the value of the ThemeResource, press F12. Visual Studio will jump to the generic.xaml file. Search TargetType="Textbox" to find the style
I am using a TextBlock and not a TextBox . Is changing the generic.xaml still an option here ?
No. But there is another way that you could change the foreground of the TextBlock when you pointer over. Please see the update.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.