UWP 自定义文本框样式

Posted

技术标签:

【中文标题】UWP 自定义文本框样式【英文标题】:UWP custom TextBox styling 【发布时间】:2021-04-06 14:41:31 【问题描述】:

我目前正在对现有 UWP 项目进行重新设计,其中几个 TextBox 控件的实现方式与以前略有不同。 Header 属性应该显示在TextBox 中,并且应该有一些其他的小调整,例如CornerRadius 等。这应该相对容易(或者我认为是这样)。凭借我在 UWP 样式方面的(诚然,略微有限的)经验,经过一番搜索,我得出的结论是,我最好的 MO 是创建一个针对 TextBox 的新样式,以便我可以指定哪个框会以这种方式运行并离开其余的按原样。

我创建了一个自定义样式,其中BorderElement 从第 0 行开始并跨越 2 行,通过从通用 XAML 文件复制TextBox 的模板来包围标题和输入文本。它看起来像这样:

<Style x:Key="InlineHeaderTextBox" TargetType="TextBox">
        <Setter Property="Foreground" Value="StaticResource ControlText" />
        <Setter Property="Background" Value="White" />
        <Setter Property="BorderBrush" Value="StaticResource ControlBorder" />
        <Setter Property="SelectionHighlightColor" Value="ThemeResource TextControlSelectionHighlightColor" />
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="FontFamily" Value="StaticResource MS-SemiBold" />
        <Setter Property="FontSize" Value="16" />
        <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Auto" />
        <Setter Property="ScrollViewer.VerticalScrollMode" Value="Auto" />
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
        <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden" />
        <Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
        <Setter Property="Padding" Value="12 6 12 6" />
        <Setter Property="HeaderTemplate">
            <Setter.Value>
                <DataTemplate>
                    <Border Background="Green">
                        <TextBlock Text="Binding" Visibility="Visible" Opacity="1"></TextBlock>
                    </Border>
                </DataTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="TextBox">
                    <Grid>
                        <Grid.Resources>
                            <Style x:Name="DeleteButtonStyle" TargetType="Button">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="Button">
                                            <Grid x:Name="ButtonLayoutGrid" BorderBrush="ThemeResource TextControlButtonBorderBrush" BorderThickness="TemplateBinding BorderThickness" Background="ThemeResource TextControlButtonBackground">

                                                <VisualStateManager.VisualStateGroups>
                                                    <VisualStateGroup x:Name="CommonStates">
                                                        <VisualState x:Name="Normal" />

                                                        <VisualState x:Name="PointerOver">
                                                            <Storyboard>
                                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonLayoutGrid" Storyboard.TargetProperty="Background">
                                                                    <DiscreteObjectKeyFrame KeyTime="0" Value="ThemeResource TextControlButtonBackgroundPointerOver" />
                                                                </ObjectAnimationUsingKeyFrames>
                                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonLayoutGrid" Storyboard.TargetProperty="BorderBrush">
                                                                    <DiscreteObjectKeyFrame KeyTime="0" Value="ThemeResource TextControlButtonBorderBrushPointerOver" />
                                                                </ObjectAnimationUsingKeyFrames>
                                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement" Storyboard.TargetProperty="Foreground">
                                                                    <DiscreteObjectKeyFrame KeyTime="0" Value="ThemeResource TextControlButtonForegroundPointerOver" />
                                                                </ObjectAnimationUsingKeyFrames>
                                                            </Storyboard>
                                                        </VisualState>

                                                        <VisualState x:Name="Pressed">
                                                            <Storyboard>
                                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonLayoutGrid" Storyboard.TargetProperty="Background">
                                                                    <DiscreteObjectKeyFrame KeyTime="0" Value="ThemeResource TextControlButtonBackgroundPressed" />
                                                                </ObjectAnimationUsingKeyFrames>
                                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonLayoutGrid" Storyboard.TargetProperty="BorderBrush">
                                                                    <DiscreteObjectKeyFrame KeyTime="0" Value="ThemeResource TextControlButtonBorderBrushPressed" />
                                                                </ObjectAnimationUsingKeyFrames>
                                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement" Storyboard.TargetProperty="Foreground">
                                                                    <DiscreteObjectKeyFrame KeyTime="0" Value="ThemeResource TextControlButtonForegroundPressed" />
                                                                </ObjectAnimationUsingKeyFrames>
                                                            </Storyboard>
                                                        </VisualState>

                                                        <VisualState x:Name="Disabled">
                                                            <Storyboard>
                                                                <DoubleAnimation Storyboard.TargetName="ButtonLayoutGrid"
                                                                    Storyboard.TargetProperty="Opacity"
                                                                    To="0"
                                                                    Duration="0" />
                                                            </Storyboard>
                                                        </VisualState>
                                                    </VisualStateGroup>
                                                </VisualStateManager.VisualStateGroups>
                                                <TextBlock x:Name="GlyphElement"
                                                    Foreground="ThemeResource TextControlButtonForeground"
                                                    VerticalAlignment="Center"
                                                    HorizontalAlignment="Center"
                                                    FontStyle="Normal"
                                                    FontSize="12"
                                                    Text="&#xE10A;"
                                                    FontFamily="ThemeResource SymbolThemeFontFamily"
                                                    AutomationProperties.AccessibilityView="Raw" />
                                            </Grid>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </Grid.Resources>

                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="Background">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="ThemeResource TextControlBackgroundPointerOver" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="HeaderContentPresenter" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="ThemeResource TextControlHeaderForegroundDisabled" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="Background">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="ThemeResource TextControlBackgroundDisabled" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderBrush">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="ThemeResource TextControlBorderBrushDisabled" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="ThemeResource TextControlForegroundDisabled" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Binding PlaceholderForeground, RelativeSource=RelativeSource TemplatedParent, TargetNullValue=ThemeResource TextControlPlaceholderForegroundDisabled" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </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 TemplatedParent, TargetNullValue=ThemeResource TextControlPlaceholderForegroundPointerOver" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="ThemeResource TextControlForegroundPointerOver" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Focused">                                    
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Binding PlaceholderForeground, RelativeSource=RelativeSource TemplatedParent, TargetNullValue=ThemeResource TextControlPlaceholderForegroundFocused" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="Background">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="ThemeResource TextControlBackgroundFocused" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderBrush">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="ThemeResource TextControlBorderBrushFocused" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="ThemeResource TextControlForegroundFocused" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="RequestedTheme">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Light" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="ButtonStates">
                                <VisualState x:Name="ButtonVisible">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="DeleteButton" Storyboard.TargetProperty="Visibility">
                                            <DiscreteObjectKeyFrame KeyTime="0">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Visible</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="ButtonCollapsed" />
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>

                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="*" />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>

                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>

                        <ContentPresenter x:Name="HeaderContentPresenter"
                            Grid.Row="0"
                            Grid.Column="0"
                            Grid.ColumnSpan="2"
                            Content="TemplateBinding Header"
                            ContentTemplate="TemplateBinding HeaderTemplate"
                            FontWeight="Normal"
                            Foreground="StaticResource ControlTextLight"
                            Background="Red"
                            Margin="12 6 12 6"
                            TextWrapping="Wrap"
                            VerticalAlignment="Top"
                            Visibility="Visible"
                            Opacity="1" 
                            x:DeferLoadStrategy="Lazy" />
                        <Border x:Name="BorderElement"
                            Grid.Row="0"
                            Grid.Column="0"
                            Grid.RowSpan="2"
                            Grid.ColumnSpan="2"
                            Background="TemplateBinding Background"
                            BorderBrush="TemplateBinding BorderBrush"
                            BorderThickness="1"
                            CornerRadius="4"
                            Control.IsTemplateFocusTarget="True"
                            MinWidth="ThemeResource TextControlThemeMinWidth"
                            MinHeight="ThemeResource TextControlThemeMinHeight" />
                        <ScrollViewer x:Name="ContentElement"
                            Grid.Row="1"
                            Grid.Column="0"
                            HorizontalScrollMode="TemplateBinding ScrollViewer.HorizontalScrollMode"
                            HorizontalScrollBarVisibility="TemplateBinding ScrollViewer.HorizontalScrollBarVisibility"
                            VerticalScrollMode="TemplateBinding ScrollViewer.VerticalScrollMode"
                            VerticalScrollBarVisibility="TemplateBinding ScrollViewer.VerticalScrollBarVisibility"
                            IsHorizontalRailEnabled="TemplateBinding ScrollViewer.IsHorizontalRailEnabled"
                            IsVerticalRailEnabled="TemplateBinding ScrollViewer.IsVerticalRailEnabled"
                            IsDeferredScrollingEnabled="TemplateBinding ScrollViewer.IsDeferredScrollingEnabled"
                            Margin="TemplateBinding BorderThickness"
                            Padding="TemplateBinding Padding"
                            IsTabStop="False"
                            AutomationProperties.AccessibilityView="Raw"
                            ZoomMode="Disabled" />
                        <TextBlock x:Name="PlaceholderTextContentPresenter"
                            Grid.Row="1"
                            Grid.Column="0"
                            Grid.ColumnSpan="2"
                            Foreground="Binding PlaceholderForeground, RelativeSource=RelativeSource TemplatedParent, TargetNullValue=ThemeResource TextControlPlaceholderForeground"
                            Margin="TemplateBinding BorderThickness"
                            Padding="TemplateBinding Padding"
                            Text="TemplateBinding PlaceholderText"
                            TextAlignment="TemplateBinding TextAlignment"
                            TextWrapping="TemplateBinding TextWrapping"
                            IsHitTestVisible="False" />
                        <Button x:Name="DeleteButton"
                            Grid.Row="1"
                            Grid.Column="1"
                            Style="StaticResource DeleteButtonStyle"
                            BorderThickness="TemplateBinding BorderThickness"
                            Margin="ThemeResource HelperButtonThemePadding"
                            IsTabStop="False"
                            Visibility="Collapsed"
                            AutomationProperties.AccessibilityView="Raw"
                            FontSize="TemplateBinding FontSize"
                            MinWidth="34"
                            VerticalAlignment="Stretch" />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

文本框在相关面板上实现如下:

<TextBox Name="test1" Text="1234" Style="StaticResource InlineHeaderTextBox" PlaceholderText="test placeholder text" Header="test header text" InputScope="Digits" MaxLength="10" Width="200" RelativePanel.Below="P3R3C1" />
<TextBox Name="test2" Text="5678" PlaceholderText="test placeholder text" Header="test header text" InputScope="Digits" MaxLength="10" Width="200" RelativePanel.Below="test1" />

结果看起来不错,但标题文本及其背景看起来比预期的要浅(褪色),并在文本框获得焦点时消失!

作为参考,我在它下面添加了第二个文本框,没有任何样式。对于此框,标题文本看起来不褪色并且保持静止,即使在聚焦框时也是如此。我尝试摆弄 VisualStates 并使用自定义 HeaderTemplate,甚至添加了红色和绿色背景以查看标题文本会发生什么,但似乎无法弄清楚是什么原因导致使用我的自定义样式的标题文本以这种方式表现.

如何设置此标题的样式,即使在获得焦点时也能保持不变,并像第二个文本框一样清晰显示(不会略微褪色)?

【问题讨论】:

【参考方案1】:

问题是由模板中HeaderContentPresenter 上方的BorderElement 引起的。默认情况下,它只是部分不透明,但足以使您的标题看起来褪色。聚焦时,其背景设置为完全不透明,因此您的标题会消失。

要解决此问题,只需将模板中的BorderElement 切换到HeaderContentPresenter 上方即可:

...
<Border x:Name="BorderElement"
    Grid.Row="0"
    Grid.Column="0"
    Grid.RowSpan="2"
    Grid.ColumnSpan="2"
    Background="TemplateBinding Background"
    BorderBrush="TemplateBinding BorderBrush"
    BorderThickness="1"
    CornerRadius="4"
    Control.IsTemplateFocusTarget="True"
    MinWidth="ThemeResource TextControlThemeMinWidth"
    MinHeight="ThemeResource TextControlThemeMinHeight" />
<ContentPresenter x:Name="HeaderContentPresenter"
    Grid.Row="0"
    Grid.Column="0"
    Grid.ColumnSpan="2"
    Content="TemplateBinding Header"
    ContentTemplate="TemplateBinding HeaderTemplate"
    FontWeight="Normal"
    Foreground="StaticResource ControlTextLight"
    Background="Red"
    Margin="12 6 12 6"
    TextWrapping="Wrap"
    VerticalAlignment="Top"
    Visibility="Visible"
    Opacity="1" 
    x:DeferLoadStrategy="Lazy" />
...

【讨论】:

以上是关于UWP 自定义文本框样式的主要内容,如果未能解决你的问题,请参考以下文章

axure自定义文本框样式

axure切换焦点文本框样式

uwp xaml - 将自定义输入格式设置为文本框

WPF自定义控件与样式-TextBox & RichTextBox & PasswordBox样式水印Label标签功能扩展

JavaScript 自定义文本框光标——初级版

文本框样式width100%超过父容器