Non-Destructive Me

WPF TextBox with Watermark

WPF has simply the most powerful templating and visual engine that is available at the moment for designing your UI.  A requirement came up in a project that required a watermarked textbox for displaying some contextual help.

With WPF this becomes trivial.  So lets first create a control that extends TextBox to add our watermark text property.

    public class TextBoxWithHelp : TextBox
    {
        public string HelpText
        {
            get {return (string)GetValue(HelpTextProperty); }
            set { SetValue(HelpTextProperty, value); }
        }
 
        public static readonly DependencyProperty HelpTextProperty =
            DependencyProperty.Register("HelpText", 
                 typeof(string), 
                 typeof(TextBoxWithHelp), 
                 new PropertyMetadata(String.Empty));
    }

This code creates us a control that functions exactly the same as a TextBox but allows us a DependencyProperty we can bind some watermark to. So now can we use this control and style it as needed.  And this becomes a pretty simple task.  What we will do is take the default style of a TextBox and add an additional element and some additional triggers.  For brevity, I’ve omitted all the TemplateBindings which are not required for this example.

<Style TargetType="{x:Type local:TextBoxWithHelp}">
    <Setter 
        Property="Template">
        <Setter.Value>
            <ControlTemplate 
                TargetType="{x:Type local:TextBoxWithHelp}">
                <Microsoft_Windows_Themes:ListBoxChrome  
                    x:Name="Bd" 
                    SnapsToDevicePixels="true" >
                    <Grid>
                        <ScrollViewer 
                            x:Name="PART_ContentHost" />
                        <TextBlock 
                            x:Name="PART_HelpTextElement" 
                            Text="{TemplateBinding HelpText}" 
                            Visibility="Collapsed"/>
                    </Grid>
                </Microsoft_Windows_Themes:ListBoxChrome>
                <ControlTemplate.Triggers>
                    <Trigger 
                        Property="Text" 
                        Value="">
                        <Setter 
                            TargetName="PART_HelpTextElement" 
                            Property="Visibility" 
                            Value="Visible"/>
                    </Trigger>
                    <Trigger 
                        Property="Text" 
                        Value="{x:Null}">
                        <Setter 
                            TargetName="PART_HelpTextElement" 
                            Property="Visibility" 
                            Value="Visible"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

What we have changed in the base TextBox style is the addition of the “PART_HelpTextElement” TextBlock.  This is the element that the watermark will sit in and is collapsed by default.  In the control template triggers collection, you’ll notice that the moment that the control’s Text Property is “” or null, the watermark TextBlock’s visibility property will be set to Visible and the watermark will become visible.

This is a simple solution for a problem that occurs quite frequently.  Because of the  power of WPF, you are able to style the watermark in any fashion you require.

Posted: 01-02-2009 11:14 by Ray Booysen | with 11 comment(s)
Filed under: , , ,

Comments

DotNetKicks.com said:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# January 3, 2009 5:00 AM

Russell Eubanks said:

Hi Ray,

Thanks for the example. I'm trying to use your method in the following code, but can't seem to get it to work. Do you see anything I'm doing wrong?

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Media;

namespace REUserControlLibrary

{

   public class REWatermarkedTextBox : TextBox

   {

       public string Watermark

       {

           get

           {

               return (string)GetValue(WatermarkProperty);

           }

           set

           {

               SetValue(WatermarkProperty, value);

           }

       }

       public static readonly DependencyProperty WatermarkProperty =

           DependencyProperty.Register("Watermark",

           typeof(string),

           typeof(REWatermarkedTextBox),

           new PropertyMetadata(String.Empty));

   }

}

<LinearGradientBrush x:Key="TextBoxBorder" EndPoint="0,20" StartPoint="0,0" MappingMode="Absolute">

       <GradientStop Color="#ABADB3" Offset="0.05"/>

       <GradientStop Color="#E2E3EA" Offset="0.07"/>

       <GradientStop Color="#E3E9EF" Offset="1"/>

   </LinearGradientBrush>

Thanks for taking a look at this.

Regards,

Russell

# May 8, 2009 6:34 AM

Ray Booysen said:

Hi Russel

What error are you getting?  Can I see your XAML?  If you want, get in touch with me at ray@vistasquad.co.uk so we can discuss further.

Cheers

Ray

# May 8, 2009 6:56 AM

russelle said:

I emailed you the code. I'm not getting an error, just no watermark in the TextBox. Thanks.

Russell

# May 8, 2009 6:58 AM

Andre said:

Hi Ray,

Thanks for the great tip. I am trying to implement textbox with Watermark in my project, however I have a problem with themes. I use themes from WPF toolkit (ShinyBlue currently), but because in your approach ControlTemplate gets overriden I get Watermark behaviour Ok, but the theme is gone. Is there a way to make these two coexist? I don't want hardcode ShinyBlue's textbox style in my style though, as I want to be able to switch themes.

Any suggestions?

Thanks.

Andre.

# June 9, 2009 1:56 AM

Ray Booysen said:

Hi Andre

Can you email me at ray@vistasquad.co.uk so we can discuss this further?

Cheers

Ray

# June 9, 2009 5:26 AM

Andre said:

Hi Ray,

I have sent you email. Looking forward to hear from you.

Cheers,

Andre.

# June 10, 2009 12:10 AM

Mel said:

Hey Ray,

Could you show me how you're referencing the namespace for the Microsoft_Windows_Themes:ListBoxChrome element in your XAML?

Thanks,

Mel

# June 29, 2009 7:30 PM

Mel said:

Oh nevermind, it's one of the PresentationFramework.* assemblies.

I used:

xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"

# June 29, 2009 7:34 PM

IVan said:

Did you have any issues when you leave the HelpText property empty??

# July 2, 2009 9:17 PM

Ray Booysen said:

Hi IVan

Not that I have seen, what issues are you seeing?

# July 3, 2009 3:47 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)