Saturday, November 21, 2009

Watermark effect for WPF’s TextBox and ComboBox controls

Update

I have updated this sample to contain the watermark effect for the ComboBox control too. Its implementation is almost identical to the TextBox’s one. Here are the screens showing the effect being applied to the ComboBox control:

image image

And here comes the original post.

In this post I’m going to show a nice watermark effect for WPF’s TextBox control. Sample application (link to the sources at the end of this post) looks as follows:

image image

As you can see, non focused text box containing no text, displays a useful clue to the user informing what kind of information should be entered. Focusing a textbox immediately removes that clue.

There are probably as many ways to introduce such a behavior as there are developers all over the world :) However, one particular solution is to be considered in this case. The above effect has been achieved using adorners. The adorner used for the effect contains a single TextBlock with optional style applied to it. A good introduction to adorners is available here.

The effect has been implemented using attached behavior pattern, so no inheritance was introduced.

Using the behavior is a piece of cake. Here’s how to apply the behavior to a TextBox control:

<TextBox Height="25" Margin="0,5,0,5"
Behaviors:WatermarkTextBoxBehavior.EnableWatermark="True"
Behaviors:WatermarkTextBoxBehavior.Label="Label"
Behaviors:WatermarkTextBoxBehavior.LabelStyle="{StaticResource watermarkLabelStyle2}" />

The first property, EnableWatermark, as its name suggests, enables the effect ;) The Label property holds the text to be displayed as a watermark. Finally, the LabelStyle property contains the style to be applied to the label. And the style, defined in Window’s Resources collection, might look as follows:


<Style x:Key="watermarkLabelStyle">
<Setter Property="TextBlock.Foreground" Value="{x:Static SystemColors.ControlDarkBrush}" />
<Setter Property="FrameworkElement.Opacity" Value="0.8" />
<Setter Property="TextBlock.FontSize" Value="12" />
<Setter Property="TextBlock.FontStyle" Value="Italic" />
<Setter Property="TextBlock.Margin" Value="8,4,4,4" />
</Style>

I’m not going to discuss any implementation details here since the adorner itself is trivial, and the behavior is quite easy as well. Maybe I will tell you that the watermark is displayed when the TextBox fires its Loaded event. Why is that important to know? Keep reading… ;P Besides, skim the code and you immediately know how stuff works :)

Finally, one thing, which deserves to give attention to, is control loading and the adorner layer (adorner layer is a place always rendered over the control where all adorners for that control get displayed). When a control is contained within a portion of UI that is being displayed, but the control itself is not visible, it normally fires Loaded event, but because it is not visible, it has no adorner layer! So don’t assume that call to AdornerLayer.GetAdornerLayer(Visual) always returns the adorner layer! This is why the sample shown above contains a TabControl – the second tab, named Tab 2, contains a TextBox with the watermark effect applied to it, and this still works :)


As always, code sample is available through my Code Gallery here.

8 comments:

Rayne said...

Excellent! This is exactly what I have been searching for the last 2 days.

Mike said...

Great post. Thanks for sharing.

One thing, the code does not handle visibility very well.

All that has to be done is attach to the IsVisibleChanged event and also change the "UpdateAdorner" method to check the visibility of the parent.

Cheers

amit said...

I loved you Watermark effect but i having problem using it with Flowdirection Right To Left - the text is mirroring. can you help me fix it ?

Gene said...

Thanks for the handy code. I saw one issue though, looking through the code. Using DependencyPropertyDescriptor.AddValueChanged() causes a memory leak because the control your behavior is attached to will never be disposed. This article explains it well: http://sharpfellows.com/post/Memory-Leaks-and-Dependency-Properties.aspx

Pravesh Singh said...

Hi,

I was reading your article and I would like to appreciate you for making it very simple and understandable. This article gives me a basic idea of WPF Combox Control. It helped me lot in completing my task. There are some articles too which helped me lot as....
http://msdn.microsoft.com/en-us/library/ms753382%28v=vs.85%29.aspx

http://mindstick.com/Articles/347881ff-3980-4d5a-9481-42e34a8d8c0e/?Combo%20Box%20control%20in%20WPF

http://www.c-sharpcorner.com/uploadfile/mahesh/wpf-combobox/

Bennie Gouws said...

Awesome article!

I've noticed that it doesn't play nice when you set Vertical or Horizontal alignment in the style, but by removing the MeasureOverride in TextBlockAdorner solves this issue.

So that when you have it set to center, it will be centered on the actual control instead of the window.

Uchiha Itachi said...

WinForms UI combobox control

sklabak said...

Does anyone have a fix for the memory leak Gene mentioned.