uwp - 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>
当我单击文本“单击此处”时,此方法有效,我确实看到该链接在浏览器中打开。但是,我想通过操纵视觉状态来完全模拟超链接控件。我希望文本的前景色在悬停或按下时改变。我正在考虑使用视觉状态。这是个好主意吗?我该怎么做?
解决方案
如果要在悬停或按下时更改文本的颜色,则需要创建 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);
}
推荐阅读
- javascript - 如何在 JS 中对二进制图像进行 base64 编码以供浏览器显示
- websocket - Stompclient/ SockJs - 通过 1-1 代理通道多次接收消息
- javascript - 在 svelte 中使用后退按钮事件监听器
- c# - 如何在 WinForms 中显示/隐藏对象
- python - 如何检查 Python 字典中是否存在定义和键
- php - 从 mysql 获取数据到电子邮件的问题
- javascript - 如何在对象键中分配变量值?
- scala - Scala序列组通过枚举详尽
- fuzzy-search - 比较数百万个感知哈希
- c# - 如何在 resx 文件中写入换行符 \n?