在Xaml中如何实现带有水印的文本框,在第一次输入后水印消失

4
在文本框中提供一个描述,第一次输入后就消失。这应该为用户提供一点帮助,告诉他应该在文本字段中输入什么,例如水印。

可能是WPF中的水印TextBox的重复问题。 - user7116
这个回答解决了你的问题吗?水印/提示文本/占位符文本框 - StayOnTarget
4个回答

7
我已根据提供的链接从anvarbek raupov制作了一个完整的示例(http://blogs.windowsclient.net/swt62/archive/2009/05/10/wpf-textbox-watermark-the-easy-way.aspx)。关键是在文本框上方添加一个额外的标签,只有当文本框内容为空且未聚焦时才显示。该条件使用触发器在XAML中实现。我的示例样式使文本框的样式保持不变。
示例包括一个注释的资源字典(WatermarkResource.xaml)和一个具有普通文本框和水印文本框的MainWindow.xaml。代码后端仅进行初始化,并且与向导生成的WPF应用程序相同。
这是WatermarkResource.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:mwt="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <!-- Add TargetType="{x:Type TextBox}" to make this style the default for all TextBoxes. -->
    <Style x:Key="WatermarkedTextBox" >
        <Setter Property="Control.Template" >
            <Setter.Value>
                <ControlTemplate TargetType="TextBox" >
                    <!-- Template derived from Default TextBoxBase. -->
                    <!-- Added the Label InternalWatermarkLabel together with the surrounding Grid. -->
                    <Grid>
                        <mwt:ListBoxChrome Name="Bd"
                                           Background="{TemplateBinding Panel.Background}" 
                                           BorderBrush="{TemplateBinding Border.BorderBrush}"
                                           BorderThickness="{TemplateBinding Border.BorderThickness}"
                                           RenderMouseOver="{TemplateBinding UIElement.IsMouseOver}" 
                                           RenderFocused="{TemplateBinding UIElement.IsKeyboardFocusWithin}" 
                                           SnapsToDevicePixels="True">
                            <ScrollViewer Name="PART_ContentHost"
                                          SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"
                                          />
                        </mwt:ListBoxChrome>
                        <Label x:Name="InternalWatermarkLabel" 
                               Content="{TemplateBinding Tag}" 
                               Visibility="Collapsed" Focusable="False"
                               Foreground="Silver"
                               Background="Transparent"
                               />
                    </Grid>
                    <ControlTemplate.Triggers>
                        <!-- The multitrigger is responsible for showing and hiding the watermark. -->
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="IsFocused" Value="False" />
                                <Condition Property="Text" Value="" />
                            </MultiTrigger.Conditions>
                            <MultiTrigger.Setters>
                                <Setter Property="Visibility" TargetName="InternalWatermarkLabel"
                                        Value="Visible" />
                            </MultiTrigger.Setters>
                        </MultiTrigger>
                        <!-- This trigger mimics the default behavior. -->
                        <Trigger Property="UIElement.IsEnabled" Value="False" >
                            <Setter Property="Panel.Background" TargetName="Bd"
                                    Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
                            <Setter Property="TextElement.Foreground"
                                    Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

这是MainWindow.xaml文件内容:

<Window x:Class="WpfWatermark.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Watermark, the XAML way" Height="120" Width="400" >
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="WatermarkResource.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150" />
            <ColumnDefinition Width="200" />
        </Grid.ColumnDefinitions>
        <Label Grid.Row="0" Content="Textbox with Watermark:" />
        <!-- The FrameworkElement.Tag Property of .NET 4 is used to store the -->
        <!-- watermark information as the custom information about this element. -->
        <TextBox Grid.Row="0" Grid.Column="1" 
                 Tag="This is the Watermark Text." 
                 Style="{StaticResource WatermarkedTextBox}" 
                 />
        <Label Grid.Row="1" Content="A normal Textbox:" />
        <TextBox Grid.Row="1" Grid.Column="1" />
    </Grid>
</Window>

这是一个正在运行的应用程序的截图,其中水印是可见的。

水印(XAML方式)

这是带有一些文本的截图,其中水印被隐藏了。

隐藏的水印


4

这是基于.Net 4版本的Christians解决方案,它也不需要使用Aero库:

<ControlTemplate TargetType="{x:Type TextBox}">
    <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
        <Grid>
            <ScrollViewer x:Name="PART_ContentHost" Focusable="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>

            <TextBlock x:Name="InternalWatermarkLabel" 
                       Text="{TemplateBinding Tag}" 
                       Visibility="Collapsed" Focusable="False"
                       VerticalAlignment="Top" Margin=" 5 1 0 0"
                       Foreground="Silver"
                       Background="Transparent"/>
        </Grid>
    </Border>
    <ControlTemplate.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsFocused" Value="False" />
                <Condition Property="Text" Value="" />
            </MultiTrigger.Conditions>
            <MultiTrigger.Setters>
                <Setter Property="Visibility" TargetName="InternalWatermarkLabel"
                        Value="Visible" />
            </MultiTrigger.Setters>
        </MultiTrigger>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="Opacity" TargetName="border" Value="0.56"/>
        </Trigger>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="BorderBrush" TargetName="border" Value="#FF7EB4EA"/>
        </Trigger>
        <Trigger Property="IsKeyboardFocused" Value="True">
            <Setter Property="BorderBrush" TargetName="border" Value="#FF569DE5"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

谢谢您的提示。MainWindow.xaml没有改变吗?我的例子也可以在 .Net 4 下运行。或者您有不同的观察结果吗? - Christian
它能工作,但需要PresentationFramework.aero库。我使用了默认的.net 4按钮模板,并用您的想法扩展了它,基本上一切都是相同的,但要使用这个模板代替。 - Sven Hecht

2

这里是一个仅使用XAML的解决方案,采用了相对基础的“机制”。

注意:可能有更好和/或更优雅的方法来做这件事,但这是我通过试验和结合在这个站点上发现的内容而得到的。

我认为它非常明确易懂,可以对一些人有用...

欢迎针对任何问题或可以改进的地方进行评论。

<Grid>
    <TextBox Name="TheField"
             HorizontalAlignment="Center"
             VerticalAlignment="Center"
             MinWidth="170"
             Background="Transparent"
             Foreground="Black"
             Margin="5,2,5,2"
             BorderThickness="0"
             FontSize="10"
             Text="{Binding THE_BOUNDED_PROPERTY, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    <TextBox HorizontalAlignment="Center"
             VerticalAlignment="Center"
             MinWidth="170"
             Foreground="#FF808080"
             Margin="5,2,5,2"
             IsHitTestVisible="False"
             BorderThickness="0"
             FontStyle="Italic"
             FontSize="10"
             Text="THE DEFAULT TEXT">
        <TextBox.Style>
            <Style TargetType="TextBox">
                <Style.Triggers>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding ElementName=TheField, Path=IsKeyboardFocusWithin}" Value="False"/>
                            <Condition Binding="{Binding ElementName=TheField, Path=Text.IsEmpty}" Value="True"/>
                        </MultiDataTrigger.Conditions>
                        <Setter Property="Visibility" Value="Visible"/>
                    </MultiDataTrigger>
                    <DataTrigger Binding="{Binding ElementName=TheField, Path=IsKeyboardFocusWithin}" Value="True">
                        <Setter Property="Visibility" Value="Hidden"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding ElementName=TheField, Path=Text.IsEmpty}" Value="False">
                        <Setter Property="Visibility" Value="Hidden"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>
</Grid>

Text.IsEmpty如何工作:IsEmpty被从CollectionView.IsEmpty中解析出来 - ΩmegaMan

2

对于任何对此问题感兴趣的人,请查看材料设计 (http://materialdesigninxaml.net/)。

通过这个库,做到这一点真的很容易:

<TextBox
x:Name="SuggestedTextBox"
materialDesign:HintAssist.Hint="My suggestion" />

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接