首页 > 解决方案 > UWP:如何使用 TextBox 控件创建自定义超链接控件?

问题描述

我想向我的 UWP 应用程序添加一个超链接,看起来像这样

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

这在浅色模式下可以正常工作,但是当我将主题更改为深色时,将鼠标悬停在超链接上,“单击此处”变得不可见,因为它与网格背景融为一体。我尝试在悬停时更改颜色,但我遇到了这篇文章更改超链接的悬停颜色,建议不要这样做。我现在正在尝试使用文本框创建自定义超链接

<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>

当我单击文本“单击此处”时,此方法有效,我确实看到该链接在浏览器中打开。但是,我想通过操纵视觉状态来完全模拟超链接控件。我希望文本的前景色在悬停或按下时改变。我正在考虑使用视觉状态。这是个好主意吗?我该怎么做?

标签: uwpuwp-xaml

解决方案


如果要在悬停或按下时更改文本的颜色,则需要创建 TextBox 的自定义样式。

首先,单击 Visual Studio 中的 Document Outline 窗口,在可视化树中找到目标 TextBox。然后右键单击文本框并将鼠标移动到选择 - Edit Template。会有一个弹出窗口,您可以选择Edit a Copy. 之后,您将看到默认的 TextBox 样式已添加到 XAML 资源中。

现在您获得了默认样式,下一步是更改 TextBox 的默认行为。默认样式中有许多视觉状态组,请确保您更改的是正确的。第一个视觉状态组属于 TextBox 的删除按钮,跳过它。转到第二个视觉状态组,找到VisualState命名的PointerOver,您将看到一个以ContentElement's foreground属性为目标的动画。这是你要找的地方,把值改成你要使用的颜色。

代码如下所示:

        <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>

更多信息请参考文档:教程:创建自定义样式

更新:

如果您现在使用 TextBlock 而不是使用 TextBox,那么您可以尝试另一种简单的方法。

您可以尝试按照以下代码进行操作:

在您的 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>

在后面的代码中:

 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);
    }

推荐阅读