Watermark / hint text / placeholder TextBox

2018-12-31 05:11发布

How can I put some text into a textbox which is removed automatically when user types something in it? (In WPF)

30条回答
一个人的天荒地老
2楼-- · 2018-12-31 05:44

This technique uses the Background property to show / hide placeholder textbox.
Placeholder is shown event when Textbox has the focus

How it works:

  • When empty, TextBox background set to Transparent to show PlaceHolder text.
  • When not empty background set to White to cover up PlaceHolder text.

Here is basic example. For my own purposes I turned this into a UserControl.

<Grid>
    <Grid.Resources>
        <ux:NotEmptyConverter x:Key="NotEmptyConverter" />

        <Style TargetType="{x:Type Control}" x:Key="DefaultStyle">
            <Setter Property="FontSize" Value="20" />
            <Setter Property="Margin" Value="10"/>
            <Setter Property="VerticalAlignment" Value="Center"></Setter>
            <Setter Property="VerticalContentAlignment" Value="Center"></Setter>
        </Style>

        <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource DefaultStyle}"></Style>

    </Grid.Resources>

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <TextBox Grid.Row="0" Text="Placeholder Text Is Here" Foreground="DarkGray" />
    <TextBox Grid.Row="0" Name="TextBoxEdit" 
            Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
        <TextBox.Style>
            <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource DefaultStyle}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Path=FirstName.Length, FallbackValue=0, TargetNullValue=0}" Value="0">
                        <Setter Property="Background" Value="Transparent"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding Path=FirstName, FallbackValue=0, TargetNullValue=0, Converter={StaticResource NotEmptyConverter}}" Value="false">
                        <Setter Property="Background" Value="White"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>
</Grid>

Here is the ValueConverter to detect non-empty strings in the DataTrigger.

public class NotEmptyConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var s = value as string;
        return string.IsNullOrEmpty(s);
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}
查看更多
一个人的天荒地老
3楼-- · 2018-12-31 05:44
namespace PlaceholderForRichTexxBoxInWPF
{
public MainWindow()
        {
            InitializeComponent();
            Application.Current.MainWindow.WindowState = WindowState.Maximized;// maximize window on load

            richTextBox1.GotKeyboardFocus += new KeyboardFocusChangedEventHandler(rtb_GotKeyboardFocus);
            richTextBox1.LostKeyboardFocus += new KeyboardFocusChangedEventHandler(rtb_LostKeyboardFocus);
            richTextBox1.AppendText("Place Holder");
            richTextBox1.Foreground = Brushes.Gray;
        }
 private void rtb_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            if (sender is RichTextBox)
            {
                TextRange textRange = new TextRange(richTextBox1.Document.ContentStart, richTextBox1.Document.ContentEnd); 

                if (textRange.Text.Trim().Equals("Place Holder"))
                {
                    ((RichTextBox)sender).Foreground = Brushes.Black;
                    richTextBox1.Document.Blocks.Clear();
                }
            }
        }


        private void rtb_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            //Make sure sender is the correct Control.
            if (sender is RichTextBox)
            {
                //If nothing was entered, reset default text.
                TextRange textRange = new TextRange(richTextBox1.Document.ContentStart, richTextBox1.Document.ContentEnd); 

                if (textRange.Text.Trim().Equals(""))
                {
                    ((RichTextBox)sender).Foreground = Brushes.Gray;
                    ((RichTextBox)sender).AppendText("Place Holder");
                }
            }
        }
}
查看更多
闭嘴吧你
4楼-- · 2018-12-31 05:45

You can keep a seperate value for the entered text and you can set it along with the "Text" field of the text box in the "GotFocus" and "LostFocus" events. When you get the focus, you'll want to clear the text box if there is no value. And when you loss the focus, you'll want to set the get the "Text" value from the text box and then reset the "Text" value of the text box to the place holder if it is empty.

private String username = "";

private void usernameTextBox_GotFocus(object sender, RoutedEventArgs e) {
  if (String.IsNullOrEmpty(username)) {
    usernameTextBox.Text = "";
  }
}

private void usernameTextBox_LostFocus(object sender, RoutedEventArgs e) {
  username = usernameTextBox.Text;
  if (String.IsNullOrEmpty(usernameTextBox.Text)) {
    usernameTextBox.Text = "Username";
  }
}

Then you just have to make sure that the "Text" value of the text box is initialized to the place holder text.

<TextBox x:Name="usernameTextBox" Text="Username" GotFocus="usernameTextBox_GotFocus" LostFocus="usernameTextBox_LostFocus" />

You can further extract this into a class that extends the "TextBox" class and then reuse it through out your project.

namespace UI {
  public class PlaceholderTextBox : TextBox {
    public String Value { get; set; }
    public String PlaceholderText { get; set; }
    public Brush PlaceholderBrush { get; set; }
    private Brush ValuedBrush { get; set; }

    public PlaceholderTextBox() : base() {}

    protected override void OnInitialized(EventArgs e) {
      base.OnInitialized(e);

      ValuedBrush = this.Foreground;

      if (String.IsNullOrEmpty(this.Text)) {
        this.Text = PlaceholderText;
        this.Foreground = PlaceholderBrush;
      }
    }

    protected override void OnGotFocus(System.Windows.RoutedEventArgs e) {
      this.Foreground = ValuedBrush;
      if (String.IsNullOrEmpty(Value)) {
        this.Text = "";
      }

      base.OnGotFocus(e);
    }

    protected override void OnLostFocus(System.Windows.RoutedEventArgs e) {
      Value = this.Text;
      if (String.IsNullOrEmpty(this.Text)) {
        this.Text = PlaceholderText;
        this.Foreground = PlaceholderBrush;
      }

      base.OnLostFocus(e);
    }
  }
}

And then this can be added in the directly in the xaml.

<Window x:Class="UI.LoginWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:m="clr-namespace:UI"
        Initialized="Window_Initialized">
    <Grid>
        <m:PlaceholderTextBox x:Name="usernameTextBox" PlaceholderText="Username" PlaceholderBrush="Gray" />
    </Grid>
</Window>
查看更多
临风纵饮
5楼-- · 2018-12-31 05:46

Also, see this answer. 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.

查看更多
泛滥B
6楼-- · 2018-12-31 05:47

This is a sample which demonstrates how to create a watermark textbox in WPF:

<Window x:Class="WaterMarkTextBoxDemo.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WaterMarkTextBoxDemo"
    Height="200" Width="400">

    <Window.Resources>

        <SolidColorBrush x:Key="brushWatermarkBackground" Color="White" />
        <SolidColorBrush x:Key="brushWatermarkForeground" Color="LightSteelBlue" />
        <SolidColorBrush x:Key="brushWatermarkBorder" Color="Indigo" />

        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
        <local:TextInputToVisibilityConverter x:Key="TextInputToVisibilityConverter" />

        <Style x:Key="EntryFieldStyle" TargetType="Grid" >
            <Setter Property="HorizontalAlignment" Value="Stretch" />
            <Setter Property="VerticalAlignment" Value="Center" />
            <Setter Property="Margin" Value="20,0" />
        </Style>

    </Window.Resources>


    <Grid Background="LightBlue">

        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>

        <Grid Grid.Row="0" Background="{StaticResource brushWatermarkBackground}" Style="{StaticResource EntryFieldStyle}" >
            <TextBlock Margin="5,2" Text="This prompt dissappears as you type..." Foreground="{StaticResource brushWatermarkForeground}"
                       Visibility="{Binding ElementName=txtUserEntry, Path=Text.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}" />
            <TextBox Name="txtUserEntry" Background="Transparent" BorderBrush="{StaticResource brushWatermarkBorder}" />
        </Grid>

        <Grid Grid.Row="1" Background="{StaticResource brushWatermarkBackground}" Style="{StaticResource EntryFieldStyle}" >
            <TextBlock Margin="5,2" Text="This dissappears as the control gets focus..." Foreground="{StaticResource brushWatermarkForeground}" >
                <TextBlock.Visibility>
                    <MultiBinding Converter="{StaticResource TextInputToVisibilityConverter}">
                        <Binding ElementName="txtUserEntry2" Path="Text.IsEmpty" />
                        <Binding ElementName="txtUserEntry2" Path="IsFocused" />
                    </MultiBinding>
                </TextBlock.Visibility>
            </TextBlock>
            <TextBox Name="txtUserEntry2" Background="Transparent" BorderBrush="{StaticResource brushWatermarkBorder}" />
        </Grid>

    </Grid>

</Window>

TextInputToVisibilityConverter is defined as:

using System;
using System.Windows.Data;
using System.Windows;

namespace WaterMarkTextBoxDemo
{
    public class TextInputToVisibilityConverter : IMultiValueConverter
    {
        public object Convert( object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture )
        {
            // Always test MultiValueConverter inputs for non-null
            // (to avoid crash bugs for views in the designer)
            if (values[0] is bool && values[1] is bool)
            {
                bool hasText = !(bool)values[0];
                bool hasFocus = (bool)values[1];

                if (hasFocus || hasText)
                    return Visibility.Collapsed;
            }

            return Visibility.Visible;
        }


        public object[] ConvertBack( object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture )
        {
            throw new NotImplementedException();
        }
    }
}

Note: This is not my code. I found it here, but I think this is the best approach.

查看更多
浅入江南
7楼-- · 2018-12-31 05:49

Set up the text box with placeholder text in a soft color...

public MainWindow ( )
{
    InitializeComponent ( );
    txtInput.Text = "Type something here...";
    txtInput.Foreground = Brushes.DimGray;
}

When the text box gets the focus, clear it and change the text color

private void txtInput_GotFocus ( object sender, EventArgs e )
{
    MessageBox.Show ( "got focus" );
    txtInput.Text = "";
    txtInput.Foreground = Brushes.Red;
}
查看更多
登录 后发表回答