130

For example, Facebook has a "Search" hint text in the Search text box when the textbox is empty.

How to achieve this with WPF text boxes??

Facebook's search textbox

Louis Rhys
  • 34,517
  • 56
  • 153
  • 221
  • 2
    Try to search for "cue banner". – default locale Sep 15 '11 at 03:45
  • @MAKKAM [this MSDN article](http://msdn.microsoft.com/en-us/library/bb775793%28v=vs.85%29.aspx#cue_banner) discusses it but it doesn't show how it's done – Louis Rhys Sep 15 '11 at 03:50
  • 2
    also see http://stackoverflow.com/questions/833943/watermark-textbox-in-wpf – OneWorld Jan 12 '13 at 20:44
  • 1
    I wouldn't call what you are asking for 'hint text'. to me hint text is a popup window. none-the-less I found this question when wanting to setup placeholder text. and the answers below helped me. – steve Apr 29 '19 at 14:33
  • 1
    That is called watermark by the way – Welcor Oct 25 '19 at 19:08
  • Does this answer your question? [Watermark / hint text / placeholder TextBox](https://stackoverflow.com/questions/833943/watermark-hint-text-placeholder-textbox) – StayOnTarget Jun 24 '21 at 12:29
  • If you are using telerik with your WPF project you can use [RadWatermarkTextBox](https://docs.telerik.com/devtools/wpf/controls/radwatermarktextbox/overview). – Quraishi sazid Apr 06 '22 at 03:31

14 Answers14

183

You can accomplish this much more easily with a VisualBrush and some triggers in a Style:

<TextBox>
    <TextBox.Style>
        <Style TargetType="TextBox" xmlns:sys="clr-namespace:System;assembly=mscorlib">
            <Style.Resources>
                <VisualBrush x:Key="CueBannerBrush" AlignmentX="Left" AlignmentY="Center" Stretch="None">
                    <VisualBrush.Visual>
                        <Label Content="Search" Foreground="LightGray" />
                    </VisualBrush.Visual>
                </VisualBrush>
            </Style.Resources>
            <Style.Triggers>
                <Trigger Property="Text" Value="{x:Static sys:String.Empty}">
                    <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                </Trigger>
                <Trigger Property="Text" Value="{x:Null}">
                    <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                </Trigger>
                <Trigger Property="IsKeyboardFocused" Value="True">
                    <Setter Property="Background" Value="White" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </TextBox.Style>
</TextBox>

To increase the re-usability of this Style, you can also create a set of attached properties to control the actual cue banner text, color, orientation etc.

sellmeadog
  • 7,437
  • 1
  • 31
  • 45
  • 1
    Use IsMouseCaptured instead of IsKeyboardFocused. That's how a real cue banner responds. – Monstieur Mar 19 '12 at 05:01
  • 6
    If anybody wondered how to use attached properties to increase Style reusability, see: http://stackoverflow.com/a/650620/724944 – surfen Mar 27 '12 at 22:38
  • @Kurian IsMouseCaptured will make the cue disappear only when you click it with a mouse, but will appear again when you release the mouse button. It doesn't look good. IsMouseOver wouldn't be good either (keyboard has focus but mouse pointer is elsewhere => cue displayed). Most cue banners use IsKeyboardFocused (Facebook for example) and I think it's fine. Alternative solution would be to use both triggers: (IsMouseOver OR IsKeyboardFocused) – surfen Mar 28 '12 at 01:36
  • @surfen No. IsMouseCaptured is how all Microsoft software cue banners behave. Take the Windows login box: The cue banner only disappears when you enter text or when you click and hold inside it. – Monstieur Mar 28 '12 at 05:21
  • @Kurian I see the point. I agree that it sometimes makes sense to display cue even when keyboard has focus (eg. when it's the default input control on a form - like login, start menu search). But the cue banner on Windows login box, after clicking with mouse appears again only after you mouse-out from the textbox. This kind of behaviour would require more complex show/hide logic (keeping the state) - maybe with the use of EventTrigger + animation, or additional code-behind. I think it's not worth the effort. "Show cue when no text" rule is enough in most such cases. – surfen Mar 28 '12 at 10:20
  • @sellmeadog: May I ask you to have a look at my question http://stackoverflow.com/questions/18843737/attached-property-with-watermark-textbox . I tried to add an attached property but I cant make it work. – Rolfi Sep 18 '13 at 09:35
  • +1: This worked great, and it's the only WPF-relevant answer (I'd +2 it if I could!). Thanks @sellmeadog! – BrainSlugs83 Dec 20 '13 at 17:42
  • 27
    The solution should be Hint="Please enter your text" not 20 elements... Alas, this is not supported by the legendary built in text box... – Lzh Nov 29 '14 at 12:54
  • Is there a way to bind the cue banner text to the control/window data context; I tried Content="{Binding Path=CueBannerText}" where CueBannerText is a public string property in the attached view model, Visual Studio recognizes it in IntelliSense but the text does not show in the text box. Is it some spell based on RelativeSource, or is it even possible to bind properties from resources in XAML? Thanks! – George S. Feb 03 '16 at 22:32
  • 8
    While this approach may be okay for default conditions, it doesn't work when your textbox already contains a background brush or the form background is not of the same color like the text box. – LWChris May 01 '16 at 00:40
  • XAML is a perfect example against DRY principle. – M.kazem Akhgary Dec 26 '18 at 18:04
  • ALready have a background? You can wrap the textbox with any panel or grid and set your backgorund there. Set background of textblock/label inside visual brush to Transparent. – Meow Cat 2012 Aug 08 '19 at 04:05
  • @sellmeadog Can you advise on adding properties? No working for me: https://stackoverflow.com/questions/59299659/parametrized-style-to-display-hint-text-in-wpf-textbox – IgorStack Dec 12 '19 at 07:37
  • @IgorStack unfortunately, no. I haven't worked with WPF/XAML in over 6 years, I don't know what has changed. It amazes me that this is answer has any relevance today and that Microsoft just hasn't added a `Placeholder` property to the TextBox component. – sellmeadog Dec 12 '19 at 21:25
70

This is my simple solution, adapted from Microsoft (https://code.msdn.microsoft.com/windowsapps/How-to-add-a-hint-text-to-ed66a3c6)

    <Grid Background="White" HorizontalAlignment="Right" VerticalAlignment="Top"  >
        <!-- overlay with hint text -->
        <TextBlock Margin="5,2" MinWidth="50" Text="Suche..." 
                   Foreground="LightSteelBlue" Visibility="{Binding ElementName=txtSearchBox, Path=Text.IsEmpty, Converter={StaticResource MyBoolToVisibilityConverter}}" IsHitTestVisible="False"/>
        <!-- enter term here -->
        <TextBox MinWidth="50" Name="txtSearchBox" Background="Transparent" />
    </Grid>
JTIM
  • 2,774
  • 1
  • 34
  • 74
Martin Schmidt
  • 1,331
  • 10
  • 5
17

what about using materialDesign HintAssist ? i'm using this which also you can add floating hint too :

<TextBox Width="150" Height="40" Text="hello" materialDesign:HintAssist.Hint="address"  materialDesign:HintAssist.IsFloating="True"></TextBox>

i installed Material Design with Nuget Package there is installation guide in documentation link

Mahdi Khalili
  • 1,025
  • 15
  • 32
9

Do it in the code-behind by setting the text color initially to gray and adding event handlers for gaining and losing keyboard focus.

TextBox tb = new TextBox();
tb.Foreground = Brushes.Gray;
tb.Text = "Text";
tb.GotKeyboardFocus += new KeyboardFocusChangedEventHandler(tb_GotKeyboardFocus);
tb.LostKeyboardFocus += new KeyboardFocusChangedEventHandler(tb_LostKeyboardFocus);

Then the event handlers:

private void tb_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    if(sender is TextBox)
    {
        //If nothing has been entered yet.
        if(((TextBox)sender).Foreground == Brushes.Gray)
        {
            ((TextBox)sender).Text = "";
            ((TextBox)sender).Foreground = Brushes.Black;
        }
    }
}


private void tb_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    //Make sure sender is the correct Control.
    if(sender is TextBox)
    {
        //If nothing was entered, reset default text.
        if(((TextBox)sender).Text.Trim().Equals(""))
        {
            ((TextBox)sender).Foreground = Brushes.Gray;
            ((TextBox)sender).Text = "Text";
        }
    }
}
mxgg250
  • 705
  • 8
  • 13
  • 12
    -1 for doing it in code behind: clutters the control, and there is a high chance that this will interfere with other control logic, if not now then in the future. – AlexeiOst Jan 07 '16 at 03:30
5

You have to create a custom control by inheriting the textbox. Below link has an excellent example about the search textbox sample. Please have a look at this

http://davidowens.wordpress.com/2009/02/18/wpf-search-text-box/

Kishore Kumar
  • 21,449
  • 13
  • 81
  • 113
4

You can do in a very simple way. The idea is to place a Label in the same place as your textbox. Your Label will be visible if textbox has no text and hasn't the focus.

 <Label Name="PalceHolder"  HorizontalAlignment="Left" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Height="40" VerticalAlignment="Top" Width="239" FontStyle="Italic"  Foreground="BurlyWood">PlaceHolder Text Here
  <Label.Style>
    <Style TargetType="{x:Type Label}">
      <Setter Property="Visibility" Value="Hidden"/>
      <Style.Triggers>
        <MultiDataTrigger>
          <MultiDataTrigger.Conditions>
            <Condition Binding ="{Binding ElementName=PalceHolder, Path=Text.Length}" Value="0"/>
            <Condition Binding ="{Binding ElementName=PalceHolder, Path=IsFocused}" Value="False"/>
          </MultiDataTrigger.Conditions>
          <Setter Property="Visibility" Value="Visible"/>
        </MultiDataTrigger>
      </Style.Triggers>
    </Style>
  </Label.Style>
</Label>
<TextBox  Background="Transparent" Name="TextBox1" HorizontalAlignment="Left" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Height="40"TextWrapping="Wrap" Text="{Binding InputText,Mode=TwoWay}" VerticalAlignment="Top" Width="239" />

Bonus:If you want to have default value for your textBox, be sure after to set it when submitting data (for example:"InputText"="PlaceHolder Text Here" if empty).

makertoo
  • 166
  • 1
  • 11
4

I once got into the same situation, I solved it following way. I've only fulfilled the requirements of a hint box, you can make it more interactive by adding effects and other things on other events like on focus etc.

WPF CODE (I've removed styling to make it readable)

<Grid Margin="0,0,0,0"  Background="White">
    <Label Name="adminEmailHint" Foreground="LightGray" Padding="6"  FontSize="14">Admin Email</Label>
    <TextBox Padding="4,7,4,8" Background="Transparent" TextChanged="adminEmail_TextChanged" Height="31" x:Name="adminEmail" Width="180" />
</Grid>
<Grid Margin="10,0,10,0" Background="White" >
    <Label Name="adminPasswordHint" Foreground="LightGray" Padding="6"  FontSize="14">Admin Password</Label>
    <PasswordBox Padding="4,6,4,8" Background="Transparent" PasswordChanged="adminPassword_PasswordChanged" Height="31" x:Name="adminPassword" VerticalContentAlignment="Center" VerticalAlignment="Center" Width="180" FontFamily="Helvetica" FontWeight="Light" FontSize="14" Controls:TextBoxHelper.Watermark="Admin Password"  FontStyle="Normal" />
</Grid>

C# Code

private void adminEmail_TextChanged(object sender, TextChangedEventArgs e)
    {
        if(adminEmail.Text.Length == 0)
        {
            adminEmailHint.Visibility = Visibility.Visible;
        }
        else
        {
            adminEmailHint.Visibility = Visibility.Hidden;
        }
    }

private void adminPassword_PasswordChanged(object sender, RoutedEventArgs e)
    {
        if (adminPassword.Password.Length == 0)
        {
            adminPasswordHint.Visibility = Visibility.Visible;
        }
        else
        {
            adminPasswordHint.Visibility = Visibility.Hidden;
        }
    }
Mohammad Mahroz
  • 432
  • 4
  • 14
2

Another approach ;-)

this works also with PasswordBox. If you want to use it with TextBox, simply exchange PasswordChangedwith TextChanged.

XAML:

<Grid>
    <!-- overlay with hint text -->
    <TextBlock Margin="5,2"
                Text="Password"
                Foreground="Gray"
                Name="txtHintPassword"/>
    <!-- enter user here -->
    <PasswordBox Name="txtPassword"
                Background="Transparent"
                PasswordChanged="txtPassword_PasswordChanged"/>
</Grid>

CodeBehind:

private void txtPassword_PasswordChanged(object sender, RoutedEventArgs e)
{
    txtHintPassword.Visibility = Visibility.Visible;
    if (txtPassword.Password.Length > 0)
    {
        txtHintPassword.Visibility = Visibility.Hidden;
    }
}
Mat
  • 1,960
  • 5
  • 25
  • 38
  • Best and simplest solution I've found!! Thanks!! – steve Apr 29 '19 at 14:29
  • Shouldn't you add `IsHitTestVisible="False"` to be sure that where ever the user clicks it is forwarded to the correct control? – JTIM Sep 04 '20 at 09:07
  • @JTIM of course you can do that, but because PasswordBox is the last control in the XAM,L it should be "on top" of the Z index -> a click will always focus the passwordbox, also AFAIK the TextBlock is not focusable -> the textbox (or Passwordbox) will get the focus. As the answer is 3 years old I'm not 100% sure how this works, but it does ;-) – Mat Sep 07 '20 at 14:14
1

Another solution is to use a WPF toolkit like MahApps.Metro. It has many nice features, like a text box watermark:

Controls:TextBoxHelper.Watermark="Search..."

See http://mahapps.com/controls/textbox.html

StefanG
  • 1,234
  • 2
  • 22
  • 46
0

I used the got and lost focus events:

Private Sub txtSearchBox_GotFocus(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles txtSearchBox.GotFocus
    If txtSearchBox.Text = "Search" Then
        txtSearchBox.Text = ""
    Else

    End If

End Sub

Private Sub txtSearchBox_LostFocus(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles txtSearchBox.LostFocus
    If txtSearchBox.Text = "" Then
        txtSearchBox.Text = "Search"
    Else

    End If
End Sub

It works well, but the text is in gray still. Needs cleaning up. I was using VB.NET

StarLordBlair
  • 573
  • 1
  • 11
  • 27
0
  <Grid>
    <TextBox Name="myTextBox"/>
    <TextBlock>
        <TextBlock.Style>
            <Style TargetType="TextBlock">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding ElementName=myTextBox, Path=Text.IsEmpty}" Value="True">
                        <Setter Property="Text" Value="Prompt..."/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBlock.Style>
    </TextBlock>
</Grid>
DerBesondereEin
  • 360
  • 2
  • 5
0

That's my take:

<ControlTemplate>
    <Grid>
        <Grid.Resources>
            <!--Define look / layout for both TextBoxes here. I applied custom Padding and BorderThickness for my application-->
            <Style TargetType="TextBox">
                <Setter Property="Padding" Value="4"/>
                <Setter Property="BorderThickness" Value="2"/>
            </Style>
        </Grid.Resources>

        <TextBox x:Name="TbSearch"/>
        <TextBox x:Name="TbHint" Text="Suche" Foreground="LightGray"
                 Visibility="Hidden" IsHitTestVisible="False" Focusable="False"/>
    </Grid>

    <ControlTemplate.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition SourceName="TbSearch" Property="Text" Value="{x:Static sys:String.Empty}"/>
                <Condition SourceName="TbSearch" Property="IsKeyboardFocused" Value="False"/>
            </MultiTrigger.Conditions>
            <MultiTrigger.Setters>
                <Setter TargetName="TbHint" Property="Visibility" Value="Visible"/>
            </MultiTrigger.Setters>
        </MultiTrigger>

        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition SourceName="TbSearch" Property="Text" Value="{x:Null}"/>
                <Condition SourceName="TbSearch" Property="IsKeyboardFocused" Value="False"/>
            </MultiTrigger.Conditions>
            <MultiTrigger.Setters>
                <Setter TargetName="TbHint" Property="Visibility" Value="Visible"/>
            </MultiTrigger.Setters>
        </MultiTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

Most other answers including the top one have flaws in my opinion.

This solution works under all circumstances. Pure XAML, easily reusable.

Julian
  • 265
  • 5
  • 10
-1

I accomplish this with a VisualBrush and some triggers in a Style suggested by :sellmeadog.

<TextBox>
        <TextBox.Style>
            <Style TargetType="TextBox" xmlns:sys="clr-namespace:System;assembly=mscorlib">
                <Style.Resources>
                    <VisualBrush x:Key="CueBannerBrush" AlignmentX="Left" AlignmentY="Center" Stretch="None">
                        <VisualBrush.Visual>
                            <Label Content="Search" Foreground="LightGray" />
                        </VisualBrush.Visual>
                    </VisualBrush>
                </Style.Resources>
                <Style.Triggers>
                    <Trigger Property="Text" Value="{x:Static sys:String.Empty}">
                        <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                    </Trigger>
                    <Trigger Property="Text" Value="{x:Null}">
                        <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                    </Trigger>
                    <Trigger Property="IsKeyboardFocused" Value="True">
                        <Setter Property="Background" Value="White" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>

@sellmeadog :Application running, bt Design not loading...the following Error comes: Ambiguous type reference. A type named 'StaticExtension' occurs in at least two namespaces, 'MS.Internal.Metadata.ExposedTypes.Xaml' and 'System.Windows.Markup'. Consider adjusting the assembly XmlnsDefinition attributes. 'm using .net 3.5

SUHAIL AG
  • 195
  • 1
  • 8
-13

For WPF, there isn't a way. You have to mimic it. See this example. A secondary (flaky solution) is to host a WinForms user control that inherits from TextBox and send the EM_SETCUEBANNER message to the edit control. ie.

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam);

private const Int32 ECM_FIRST = 0x1500;
private const Int32 EM_SETCUEBANNER = ECM_FIRST + 1;

private void SetCueText(IntPtr handle, string cueText) {
    SendMessage(handle, EM_SETCUEBANNER, IntPtr.Zero, Marshal.StringToBSTR(cueText));
}

public string CueText {
    get {
        return m_CueText;
    } 
    set {
        m_CueText = value;
        SetCueText(this.Handle, m_CueText);
}

Also, if you want to host a WinForm control approach, I have a framework that already includes this implementation called BitFlex Framework, which you can download for free here.

Here is an article about BitFlex if you want more information. You will start to find that if you are looking to have Windows Explorer style controls that this generally never comes out of the box, and because WPF does not work with handles generally you cannot write an easy wrapper around Win32 or an existing control like you can with WinForms.

Screenshot: enter image description here

David Anderson
  • 13,558
  • 5
  • 50
  • 76
  • 1
    wow, that looks a bit hackish.. How can I do this when creating a user control with XAML? – Louis Rhys Sep 15 '11 at 03:57
  • You don't. This is how it is done. If you want to encapsulate this, then make a user control and a CueText property, and call SetCueText in the setter. – David Anderson Sep 15 '11 at 03:58
  • I guess, OP should host winforms controls, to use this approach. Or is there a way to get textbox handle? – default locale Sep 15 '11 at 04:10
  • This seems like something that could be done declaratively with WPF, by binding against whether the text box has focus or not, etc. -- The above example is more of a WinForms approach -- it will work in WPF, but it's not the right way. – BrainSlugs83 Dec 20 '13 at 16:47
  • 3
    Sorry, but this answer is simply incorrect. Also all of the links are broken. – Forest Kunecke Aug 08 '14 at 19:30
  • FKunecke is correct. This really isn't a good approach to handling this scenario. There's really good ways to do this with pure XAML, or even better, using a behavior. – Nathan Sep 10 '14 at 13:14