views:

5906

answers:

7

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

+6  A: 

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

(This is not my code, I found it on the net, but I think this is the best approach)

<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();
        }
    }
}
M. Jahedbozorgan
How can I apply it in a passwordbox?
Sauron
A: 
<Window.Resources>

    <Style x:Key="TextBoxUserStyle" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
      <Setter Property="Foreground" Value="Black"/>
      <Setter Property="HorizontalAlignment" Value="Center"/>
      <Setter Property="VerticalContentAlignment" Value="Center"/>
      <Setter Property="Width" Value="225"/>
      <Setter Property="Height" Value="25"/>
      <Setter Property="FontSize" Value="12"/>
      <Setter Property="Padding" Value="1"/>
      <Setter Property="Margin" Value="5"/>
      <Setter Property="AllowDrop" Value="true"/>
      <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type TextBox}">
            <Border x:Name="OuterBorder" BorderBrush="#5AFFFFFF" BorderThickness="1,1,1,1" CornerRadius="4,4,4,4">
              <Border x:Name="InnerBorder" Background="#FFFFFFFF" BorderBrush="#33000000" BorderThickness="1,1,1,1" CornerRadius="3,3,3,3">
                <ScrollViewer SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" x:Name="PART_ContentHost"/>
              </Border>
            </Border>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>

    <Style x:Key="PasswordBoxVistaStyle" BasedOn="{x:Null}" TargetType="{x:Type PasswordBox}">
      <Setter Property="Foreground" Value="Black"/>
      <Setter Property="HorizontalAlignment" Value="Center"/>
      <Setter Property="VerticalContentAlignment" Value="Center"/>
      <Setter Property="Width" Value="225"/>
      <Setter Property="Height" Value="25"/>
      <Setter Property="FontSize" Value="12"/>
      <Setter Property="Padding" Value="1"/>
      <Setter Property="Margin" Value="5"/>
      <Setter Property="AllowDrop" Value="true"/>
      <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type PasswordBox}">
            <Border x:Name="OuterBorder" BorderBrush="#5AFFFFFF" BorderThickness="1,1,1,1" CornerRadius="4,4,4,4">
              <Border x:Name="InnerBorder" Background="#FFFFFFFF" BorderBrush="#33000000" BorderThickness="1,1,1,1" CornerRadius="3,3,3,3">
                <Grid>
                  <Label x:Name="lblPwd" Content="Password" FontSize="11" VerticalAlignment="Center" Margin="2,0,0,0" FontFamily="Verdana" Foreground="#828385" Padding="0"/>
                  <ScrollViewer SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" x:Name="PART_ContentHost"/>
                </Grid>
              </Border>
            </Border>
            <ControlTemplate.Triggers>
              <Trigger Property="IsFocused" Value="True">
                <Setter Property="Visibility" TargetName="lblPwd" Value="Hidden"/>
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </Window.Resources>


        <PasswordBox Style="{StaticResource PasswordBoxVistaStyle}" Margin="169,143,22,0" Name="txtPassword" FontSize="14" TabIndex="2" Height="31" VerticalAlignment="Top" />

This can help check it with your code.When applied to password box,it will show Password,which will disappear when usertypes.

abmv
A: 

here is a blog shows what to do .. there are some silverlight ones around as well

http://vistasquad.co.uk/blogs/nondestructive/archive/2009/01/02/wpf-textbox-with-watermark.aspx

76mel
+2  A: 

David Owens has a more complicated example of a full "Search Box" here.

Swythan
+9  A: 

You can create a watermark that can be added to any TextBox with an Attached Property. Here is the source for the Attached Property:

   using System;
   using System.Collections.Generic;
   using System.ComponentModel;
   using System.Windows;
   using System.Windows.Controls;
   using System.Windows.Controls.Primitives;
   using System.Windows.Documents;

   /// <summary>
   /// Class that provides the Watermark attached property
   /// </summary>
   public static class WatermarkService
   {
      /// <summary>
      /// Watermark Attached Dependency Property
      /// </summary>
      public static readonly DependencyProperty WatermarkProperty = DependencyProperty.RegisterAttached(
         "Watermark",
         typeof(object),
         typeof(WatermarkService),
         new FrameworkPropertyMetadata((object)null, new PropertyChangedCallback(OnWatermarkChanged)));

      #region Private Fields

      /// <summary>
      /// Dictionary of ItemsControls
      /// </summary>
      private static readonly Dictionary<object, ItemsControl> itemsControls = new Dictionary<object, ItemsControl>();

      #endregion

      /// <summary>
      /// Gets the Watermark property.  This dependency property indicates the watermark for the control.
      /// </summary>
      /// <param name="d"><see cref="DependencyObject"/> to get the property from</param>
      /// <returns>The value of the Watermark property</returns>
      public static object GetWatermark(DependencyObject d)
      {
         return (object)d.GetValue(WatermarkProperty);
      }

      /// <summary>
      /// Sets the Watermark property.  This dependency property indicates the watermark for the control.
      /// </summary>
      /// <param name="d"><see cref="DependencyObject"/> to set the property on</param>
      /// <param name="value">value of the property</param>
      public static void SetWatermark(DependencyObject d, object value)
      {
         d.SetValue(WatermarkProperty, value);
      }

      /// <summary>
      /// Handles changes to the Watermark property.
      /// </summary>
      /// <param name="d"><see cref="DependencyObject"/> that fired the event</param>
      /// <param name="e">A <see cref="DependencyPropertyChangedEventArgs"/> that contains the event data.</param>
      private static void OnWatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
      {
         Control control = (Control)d;
         control.Loaded += Control_Loaded;

         if (d is ComboBox || d is TextBox)
         {
            control.GotKeyboardFocus += Control_GotKeyboardFocus;
            control.LostKeyboardFocus += Control_Loaded;
         }

         if (d is ItemsControl && !(d is ComboBox))
         {
            ItemsControl i = (ItemsControl)d;

            // for Items property  
            i.ItemContainerGenerator.ItemsChanged += ItemsChanged;
            itemsControls.Add(i.ItemContainerGenerator, i);

            // for ItemsSource property  
            DependencyPropertyDescriptor prop = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, i.GetType());
            prop.AddValueChanged(i, ItemsSourceChanged);
         }
      }

      #region Event Handlers

      /// <summary>
      /// Handle the GotFocus event on the control
      /// </summary>
      /// <param name="sender">The source of the event.</param>
      /// <param name="e">A <see cref="RoutedEventArgs"/> that contains the event data.</param>
      private static void Control_GotKeyboardFocus(object sender, RoutedEventArgs e)
      {
         Control c = (Control)sender;
         if (ShouldShowWatermark(c))
         {
            RemoveWatermark(c);
         }
      }

      /// <summary>
      /// Handle the Loaded and LostFocus event on the control
      /// </summary>
      /// <param name="sender">The source of the event.</param>
      /// <param name="e">A <see cref="RoutedEventArgs"/> that contains the event data.</param>
      private static void Control_Loaded(object sender, RoutedEventArgs e)
      {
         Control control = (Control)sender;
         if (ShouldShowWatermark(control))
         {
            ShowWatermark(control);
         }
      }

      /// <summary>
      /// Event handler for the items source changed event
      /// </summary>
      /// <param name="sender">The source of the event.</param>
      /// <param name="e">A <see cref="EventArgs"/> that contains the event data.</param>
      private static void ItemsSourceChanged(object sender, EventArgs e)
      {
         ItemsControl c = (ItemsControl)sender;
         if (c.ItemsSource != null)
         {
            if (ShouldShowWatermark(c))
            {
               ShowWatermark(c);
            }
            else
            {
               RemoveWatermark(c);
            }
         }
         else
         {
            ShowWatermark(c);
         }
      }

      /// <summary>
      /// Event handler for the items changed event
      /// </summary>
      /// <param name="sender">The source of the event.</param>
      /// <param name="e">A <see cref="ItemsChangedEventArgs"/> that contains the event data.</param>
      private static void ItemsChanged(object sender, ItemsChangedEventArgs e)
      {
         ItemsControl control;
         if (itemsControls.TryGetValue(sender, out control))
         {
            if (ShouldShowWatermark(control))
            {
               ShowWatermark(control);
            }
            else
            {
               RemoveWatermark(control);
            }
         }
      }

      #endregion

      #region Helper Methods

      /// <summary>
      /// Remove the watermark from the specified element
      /// </summary>
      /// <param name="control">Element to remove the watermark from</param>
      private static void RemoveWatermark(UIElement control)
      {
         AdornerLayer layer = AdornerLayer.GetAdornerLayer(control);

         // layer could be null if control is no longer in the visual tree
         if (layer != null)
         {
            Adorner[] adorners = layer.GetAdorners(control);
            if (adorners == null)
            {
               return;
            }

            foreach (Adorner adorner in adorners)
            {
               if (adorner is WatermarkAdorner)
               {
                  adorner.Visibility = Visibility.Hidden;
                  layer.Remove(adorner);
               }
            }
         }
      }

      /// <summary>
      /// Show the watermark on the specified control
      /// </summary>
      /// <param name="control">Control to show the watermark on</param>
      private static void ShowWatermark(Control control)
      {
         AdornerLayer layer = AdornerLayer.GetAdornerLayer(control);

         // layer could be null if control is no longer in the visual tree
         if (layer != null)
         {
            layer.Add(new WatermarkAdorner(control, GetWatermark(control)));
         }
      }

      /// <summary>
      /// Indicates whether or not the watermark should be shown on the specified control
      /// </summary>
      /// <param name="c"><see cref="Control"/> to test</param>
      /// <returns>true if the watermark should be shown; false otherwise</returns>
      private static bool ShouldShowWatermark(Control c)
      {
         if (c is ComboBox)
         {
            return (c as ComboBox).Text == string.Empty;
         }
         else if (c is TextBoxBase)
         {
            return (c as TextBox).Text == string.Empty;
         }
         else if (c is ItemsControl)
         {
            return (c as ItemsControl).Items.Count == 0;
         }
         else
         {
            return false;
         }
      }

      #endregion
   }

The Attached Property uses a class called WatermarkAdorner, here is that source:

   using System.Windows;
   using System.Windows.Controls;
   using System.Windows.Data;
   using System.Windows.Documents;
   using System.Windows.Media;

   /// <summary>
   /// Adorner for the watermark
   /// </summary>
   internal class WatermarkAdorner : Adorner
   {
      #region Private Fields

      /// <summary>
      /// <see cref="ContentPresenter"/> that holds the watermark
      /// </summary>
      private readonly ContentPresenter contentPresenter;

      #endregion

      #region Constructor

      /// <summary>
      /// Initializes a new instance of the <see cref="WatermarkAdorner"/> class
      /// </summary>
      /// <param name="adornedElement"><see cref="UIElement"/> to be adorned</param>
      /// <param name="watermark">The watermark</param>
      public WatermarkAdorner(UIElement adornedElement, object watermark) :
         base(adornedElement)
      {
         this.IsHitTestVisible = false;

         this.contentPresenter = new ContentPresenter();
         this.contentPresenter.Content = watermark;
         this.contentPresenter.Opacity = 0.5;
         this.contentPresenter.Margin = new Thickness(Control.Margin.Left + Control.Padding.Left, Control.Margin.Top + Control.Padding.Top, 0, 0);

         if (this.Control is ItemsControl && !(this.Control is ComboBox))
         {
            this.contentPresenter.VerticalAlignment = VerticalAlignment.Center;
            this.contentPresenter.HorizontalAlignment = HorizontalAlignment.Center;
         }

         // Hide the control adorner when the adorned element is hidden
         Binding binding = new Binding("IsVisible");
         binding.Source = adornedElement;
         binding.Converter = new BooleanToVisibilityConverter();
         this.SetBinding(VisibilityProperty, binding);
      }

      #endregion

      #region Protected Properties

      /// <summary>
      /// Gets the number of children for the <see cref="ContainerVisual"/>.
      /// </summary>
      protected override int VisualChildrenCount
      {
         get { return 1; }
      }

      #endregion

      #region Private Properties

      /// <summary>
      /// Gets the control that is being adorned
      /// </summary>
      private Control Control
      {
         get { return (Control)this.AdornedElement; }
      }

      #endregion

      #region Protected Overrides

      /// <summary>
      /// Returns a specified child <see cref="Visual"/> for the parent <see cref="ContainerVisual"/>.
      /// </summary>
      /// <param name="index">A 32-bit signed integer that represents the index value of the child <see cref="Visual"/>. The value of index must be between 0 and <see cref="VisualChildrenCount"/> - 1.</param>
      /// <returns>The child <see cref="Visual"/>.</returns>
      protected override Visual GetVisualChild(int index)
      {
         return this.contentPresenter;
      }

      /// <summary>
      /// Implements any custom measuring behavior for the adorner.
      /// </summary>
      /// <param name="constraint">A size to constrain the adorner to.</param>
      /// <returns>A <see cref="Size"/> object representing the amount of layout space needed by the adorner.</returns>
      protected override Size MeasureOverride(Size constraint)
      {
         // Here's the secret to getting the adorner to cover the whole control
         this.contentPresenter.Measure(Control.RenderSize);
         return Control.RenderSize;
      }

      /// <summary>
      /// When overridden in a derived class, positions child elements and determines a size for a <see cref="FrameworkElement"/> derived class. 
      /// </summary>
      /// <param name="finalSize">The final area within the parent that this element should use to arrange itself and its children.</param>
      /// <returns>The actual size used.</returns>
      protected override Size ArrangeOverride(Size finalSize)
      {
         this.contentPresenter.Arrange(new Rect(finalSize));
         return finalSize;
      }

      #endregion
   }

Now you can put a watermark on any TextBox like this:

<AdornerDecorator>
   <TextBox x:Name="SearchTextBox">
      <controls:WatermarkService.Watermark>
         <TextBlock>Type here to search text</TextBlock>
      </controls:WatermarkService.Watermark>
   </TextBox>
</AdornerDecorator>

The watermark can be anything you want (text, images ...). In addition to working for TextBoxes, this watermark also works for ComboBoxes and ItemControls.

This code was adapted from this blog post.

John Myczek
+1  A: 

Well here is mine: not necessarily the best, but as it is simple it is easy to edit to your taste.

<UserControl x:Class="WPFControls.ShadowedTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFControls"
Name="Root"         >
<UserControl.Resources>
    <local:ShadowConverter x:Key="ShadowConvert"/>
</UserControl.Resources>
<Grid>
    <TextBox Name="textBox" 
             Foreground="{Binding ElementName=Root, Path=Foreground}"
             Text="{Binding ElementName=Root, Path=Text, UpdateSourceTrigger=PropertyChanged}"
             TextChanged="textBox_TextChanged"
             TextWrapping="Wrap"
             VerticalContentAlignment="Center"/>
    <TextBlock Name="WaterMarkLabel"
           IsHitTestVisible="False"
           Foreground="{Binding ElementName=Root,Path=Foreground}"
           FontWeight="Thin"
           Opacity=".345"
           FontStyle="Italic"
           Text="{Binding ElementName=Root, Path=Watermark}"
           VerticalAlignment="Center"
           TextWrapping="Wrap"
           TextAlignment="Center"
           >
        <TextBlock.Visibility>
            <MultiBinding Converter="{StaticResource ShadowConvert}">
                <Binding ElementName="textBox" Path="Text"/>
            </MultiBinding>
        </TextBlock.Visibility> 
    </TextBlock>
</Grid>

The converter, as it is written now it is not necessary that it is a MultiConverter, but in this wasy it can be extended easily

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

namespace WPFControls
{
    class ShadowConverter:IMultiValueConverter
    {
        #region Implementation of IMultiValueConverter

        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            var text = (string) values[0];
            return text == string.Empty
                       ? Visibility.Visible
                       : Visibility.Collapsed;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return new object[0];
        }

        #endregion
    }
}

and finally the code behind: using System.Windows; using System.Windows.Controls;

namespace WPFControls
{
    /// <summary>
    /// Interaction logic for ShadowedTextBox.xaml
    /// </summary>
    public partial class ShadowedTextBox : UserControl
    {
        public event TextChangedEventHandler TextChanged;

        public ShadowedTextBox()
        {
            InitializeComponent();
        }

        public static readonly DependencyProperty WatermarkProperty =
            DependencyProperty.Register("Watermark",
                                        typeof (string),
                                        typeof (ShadowedTextBox),
                                        new UIPropertyMetadata(string.Empty));

        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text",
                                        typeof (string),
                                        typeof (ShadowedTextBox),
                                        new UIPropertyMetadata(string.Empty));

        public static readonly DependencyProperty TextChangedProperty =
            DependencyProperty.Register("TextChanged",
                                        typeof (TextChangedEventHandler),
                                        typeof (ShadowedTextBox),
                                        new UIPropertyMetadata(null));

        public string Watermark
        {
            get { return (string)GetValue(WatermarkProperty); }
            set
            {
                SetValue(WatermarkProperty, value);
            }
        }

        public string Text
        {
            get { return (string) GetValue(TextProperty); }
            set{SetValue(TextProperty,value);}
        }

        private void textBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (TextChanged != null) TextChanged(this, e);
        }

        public void Clear()
        {
            textBox.Clear();
        }

    }
}
Dabblernl
A: 

hi i put this task into a behavior. so you just have to add somthing like this to your textbox

<i:Interaction.Behaviors>
         <Behaviors:TextBoxWatermarkBehavior Label="Test Watermark" LabelStyle="{StaticResource StyleWatermarkLabel}"/>
</i:Interaction.Behaviors>

you can find my blog post here

blindmeis