In Xaml TextBox with a Watermark that Disappears after First Iinput
Asked Answered
C

4

4

In a TextBox provide a description that vanishes after the first input. This should provide a little help to the user what he should enter in the text field such as Watermark.

Commander answered 8/4, 2011 at 10:14 Comment(2)
possible duplicate of Watermark TextBox in WPFAegrotat
Does this answer your question? Watermark / hint text / placeholder TextBoxSneaker
C
7

I have put together a complete example based on the provided link from anvarbek raupov (http://blogs.windowsclient.net/swt62/archive/2009/05/10/wpf-textbox-watermark-the-easy-way.aspx). The trick is to add an additional label above the text box, that is only displayed when then content of the text box is empty and not focused. The condition is implemented in XAML using Triggers. My example style lets the text box style unchanged from the normal behavior.

The example consists of a commented resource dictionary (WatermarkResource.xaml) and a MainWindow.xaml with a normal and a watermarked text box. The code behind does only the initialization and is unchanged from a wizard generated WPF application.

This is the 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>

And this is the 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>

This is a screenshot of the running application with the visible Watermark

Watermark, the XAML way

and this is with some text, where the Watermark is hidden

hidden Watermark

Commander answered 11/4, 2011 at 11:39 Comment(0)
T
4

.Net 4 Version of Christians solution, it also doesn't need the aero library:

<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>
Tophet answered 7/11, 2012 at 13:33 Comment(2)
Thank you for the hint. Is the MainWindow.xaml unchanged? My example is working with .Net 4, too. Or have you observed a different behaviour?Commander
it works but needs the PresentationFramwork.aero library. I used the default .net 4 button template and extended it with your idea, basically everything is the same but use this template instead.Tophet
R
2

Here's a XAML only solution, using rather basic "mechanisms".

Note: There are probably better and/or more elegant ways to do that, but this is what I got by trying and combining stuff I found on this site.

I thought it is quite straight forward, explicit and this can be useful to some ...

Feel free to comment on any issues or things that can be done better.

<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>
Rupture answered 10/11, 2014 at 15:12 Comment(1)
How the Text.IsEmpty works: IsEmpty is being resolved from CollectionView.IsEmptyBrazenfaced
G
2

For anyone who might be interested in this question, check material design (http://materialdesigninxaml.net/).

With this library, it is really easy :

<TextBox
x:Name="SuggestedTextBox"
materialDesign:HintAssist.Hint="My suggestion" />
Glycol answered 12/9, 2018 at 9:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.