【问题标题】:Display Header on Textbox only if text is set in UWP XAML仅当在 UWP XAML 中设置了文本时,才在文本框上显示标题
【发布时间】:2017-05-31 11:24:23
【问题描述】:

我正在尝试将 UWP 应用程序中文本框控件的外观和感觉修改为我们的设计师在草图中绘制的内容。这包括仅在文本框中未设置文本时才显示标题。

我假设我必须修改 HeaderContentPresenter 及其 DataTemplate 的可见性:

<ContentPresenter x:Name="HeaderContentPresenter" Grid.ColumnSpan="2" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Foreground="{ThemeResource TextControlHeaderForeground}" FontWeight="Normal" Margin="0,0,0,2" Grid.Row="0" x:DeferLoadStrategy="Lazy" BorderThickness="0" Visibility="Collapsed" />

我写了一个 IValueConvert 来将“空字符串”转换为可见性,但我被绑定卡住了。我看不到任何获取数据上下文的方法。

    <Setter Property="HeaderTemplate">
        <Setter.Value>
            <DataTemplate>
                <TextBlock Text="{Binding}" Foreground="{StaticResource DarkGrey}" FontSize="10" FontFamily="TheSansB4SemiLight" Visibility="{Binding Converter={StaticResource EmptyStringToVisibilityConverter}, RelativeSource={RelativeSource Self}}"/>
            </DataTemplate>
        </Setter.Value>
    </Setter>

如何访问写入在 DataTemplate 中的文本框中的文本?

谢谢

用代码更新

我想要这个

MainPage.xaml

我应用了相同样式的三个文本框。

    <TextBox Header="Username" PlaceholderText="Username" Grid.Column="1" HorizontalAlignment="Left" Height="67" Margin="20,143,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="295" Style="{StaticResource InputField}" />
    <TextBox Header="Surname" PlaceholderText="Surname" Grid.Column="1" HorizontalAlignment="Left" Height="67" Margin="20,249,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="295" Style="{StaticResource InputField}"/>
    <TextBox Header="Name" PlaceholderText="Name" Grid.Column="1" HorizontalAlignment="Left" Height="67" Margin="20,196,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="295" Style="{StaticResource InputField}"/>

Styles.xaml

ResourceDict 中的基本可共享样式,在 App.xaml 中引用。当我将文本框命名为“InputField”时,该机制开始工作,但显然我有名称冲突和奇怪的行为。

<Setter Property="HeaderTemplate">
        <Setter.Value>
            <DataTemplate>
                <TextBlock 
                    Foreground="{StaticResource DarkGrey}" 
                    FontSize="10" 
                    FontFamily="TheSansB4SemiLight"
                    Visibility="{Binding Text, Converter={StaticResource EmptyStringToVisibilityConverter}, ElementName=InputField}"
                    Text="{Binding Header, ElementName=InputField}"
                    />
            </DataTemplate>
        </Setter.Value>
    </Setter>

问题

必须命名模板中的哪些元素才能通过绑定访问?我认为它是组成文本框控件的元素之一,但我不知道如何。

<Grid><Border x:Name="BorderElement" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" Grid.Row="1" Grid.RowSpan="1" CornerRadius="5"/>
                    <ContentControl x:Name="PlaceholderTextContentPresenter" Grid.ColumnSpan="2" Content="{TemplateBinding PlaceholderText}" Foreground="{ThemeResource TextControlPlaceholderForeground}" IsHitTestVisible="False" IsTabStop="False" Margin="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Grid.Row="1"/>
                    <Button x:Name="DeleteButton" AutomationProperties.AccessibilityView="Raw" BorderThickness="{TemplateBinding BorderThickness}" Grid.Column="2" FontSize="{TemplateBinding FontSize}" IsTabStop="False" MinWidth="34" Grid.Row="1" Style="{StaticResource DeleteButtonStyle}" VerticalAlignment="Stretch" Visibility="Collapsed"/>
                    <ContentPresenter x:Name="HeaderContentPresenter" Grid.ColumnSpan="2" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Foreground="{ThemeResource TextControlHeaderForeground}" FontWeight="Normal" Margin="0,0,0,2" Grid.Row="0" x:DeferLoadStrategy="Lazy" BorderThickness="0" Visibility="Collapsed" />
                    <ScrollViewer x:Name="ContentElement" AutomationProperties.AccessibilityView="Raw" HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" IsTabStop="False" IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}" IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}" IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" Margin="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Grid.Row="1" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" ZoomMode="Disabled" Grid.ColumnSpan="2"/>
                </Grid>

【问题讨论】:

  • 附带说明:从用户体验的角度来看,这个概念很糟糕。您是否有机会说服您的设计师重新考虑这一点?
  • @ManfredRadlwimmer 感谢您的反馈。它已经在 CSS 框架中实现并且富有成效。我只需要将一些东西转移到 xaml,这比我想象的要难。你是对的,不确定我是否喜欢这个概念,但这是我现在必须实现的。
  • 这很不幸,但UX Stackexchange 上的某个人可能会给你一些关于如何改进这一点的建议。跳转文本框从来都不是一个好主意。也许隐藏标题而不是折叠它?
  • 好吧,我不能 100% 确定是否需要“跳跃”。我也认识到这一点,这就是我试图隐藏控件的原因。我对其进行了修改以保持相同的高度,我认为这是一个合理的变化。感谢您指出这一点。

标签: c# xaml uwp


【解决方案1】:

这里有 2 个解决方案。

  1. 根据转换器和 TextBox.Text 长度隐藏/显示 TextBox 标题
  2. 基于 CustomControl 和失去/获得焦点隐藏/显示文本框标题

1) 根据转换器和TextBox.Text 长度隐藏/显示TextBox Header

您需要在DataTemplate 中命名您的TextBox,以便绑定可以找到该项目并处理该值。

以下是转换器的示例。

public class DataToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        Visibility dataVisible = (value.ToString().Length == 0) ? Visibility.Visible : Visibility.Collapsed;
        return dataVisible;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

下面是我在ListView 内的DataTemplate 中使用它的方法。

<ListView ItemsSource="{Binding }">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Grid>
                <TextBox x:Name="textBox" HorizontalAlignment="Center" VerticalAlignment="Center" Width="100"  TextWrapping="Wrap" >
                    <TextBox.Header>
                        <TextBlock Text="Header String" Visibility="{Binding Text, Converter={StaticResource DataToVisibilityConverter}, ElementName=textBox}"/>
                    </TextBox.Header>
                </TextBox>
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

最终结果

编辑

更新问题后,事情变得更简单了。

2。隐藏/显示基于CustomControl和失去/获得焦点的文本框标题

您需要创建一个自定义控件来处理文本块中常见的事件。

因此,根据您的图像,当您获得焦点时,您会显示标题并失去焦点,因为占位符中有文本,所以您将其隐藏。

下面是一个完全可以做到这一点的自定义控件。

public sealed class MyTextBox : TextBox
{
    public MyTextBox()
    {
        this.DefaultStyleKey = typeof(TextBox);
        this.GotFocus += MyTextBox_GotFocus;
        this.LostFocus += MyTextBox_LostFocus;
    }

    private void MyTextBox_LostFocus(object sender, RoutedEventArgs e)
    {
        this.Header = "";
    }

    private void MyTextBox_GotFocus(object sender, RoutedEventArgs e)
    {
        this.Header = this.PlaceholderText;
    }
}

而且用法会是

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <local:MyTextBox PlaceholderText="User Name" x:Name="txtUserName" Grid.Row="0" Margin="10"/>
    <local:MyTextBox PlaceholderText="Email" x:Name="txtEmail" Grid.Row="1" Margin="10"/>
    <local:MyTextBox PlaceholderText="Password" x:Name="txtPassword" Grid.Row="2" Margin="10"/>
</Grid>

【讨论】:

  • 哦不,真的吗?这么容易?花了我几个小时:-(我认为必须有某种方法可以在不处理 x:Name 的情况下访问数据。非常感谢!
  • 啊,等一下。我想将此行为用作可共享的样式。使用元素名称时,这只适用于一个元素,而不适用于我页面上的每个文本框。任何想法 ? RelativeSource={RelativeSource TemplatedParent} 看起来很有希望,但再次不确定如何获取这些值。
  • 如果您想将其用作可共享资源,我建议您创建一个自定义控件以便它可以重复使用。
  • 是的,这是我会考虑的一种方式。但是我仍然坚持如何访问模板中的文本,因为您的建议仅适用于特定的命名控件。我真的不知道如何在我的案例中访问文本。有什么提示吗?
  • @BenAffleckIsBatman 从TextBox 获取文本的按钮在哪里?里面DataTemplate?
猜你喜欢
  • 2019-03-19
  • 1970-01-01
  • 1970-01-01
  • 2020-06-23
  • 2019-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多