Click here to Skip to main content
Click here to Skip to main content

A WatermarkTextBox in 3 lines of XAML

By , 25 Jul 2008
 

Introduction

Published examples demonstrating how to create a WatermarkTextBox in WPF, including the 'InfoTextBox' control in Kevin Moore's popular "Bag-O-Tricks" sample library always seem to involve a significant amount of code-behind, when a fully-functional version can be built with just three lines of XAML, leveraging WPF's built in BooleanToVisibilityConverter.

WaterMarkTextBoxDemo - Click to enlarge image

The complete project includes two variations on this functionality.

ScreenShot.jpg

This example stems from a discussion in a recent .NET User's Group meeting, in which several experienced WPF developers complained about the removal of the WatermarkTextBox control in the latest version of Silverlight. They all jumped to the conclusion (which I think is very common among developers) that supporting similar functionality would require "coding" a custom control of some kind.

Although this is a trivial example, I think the related question of when to use XAML vs. code (and, as possibly illustrated in my second example, knowing when you've begun to cross over into "hacking via markup"), will be one of several interesting XAML-related best practice debates on many real-world multi-developer projects -- especially when faced with a confusing mixture of seemingly random implementation choices around the third dev. cycle on a large code base.

Update: Microsoft has announced that it will add a "WaterMark" property to the TextBox in a future version of Silverlight, and has made the original WatermarkTextBox code available for download.

Other Projects by Andy L.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

AndyL2
United States United States
Member
I started out writing applications in C for a software development shop in Japan, did alot of work in C++/MFC, and some DirectX, at two Silicon Valley startups, and have been working with C# and Windows Forms ever since the release of .Net 1.0. Although I took a couple intro. to CS courses at CAL (Berkeley), my degree was actually in Asian Studies, and I learned to program "in the trenches". I was also the .Net evangelist at my most recent company, authoring internal white papers on .Net, sending out a weekly ".Net FYI" e-mail, conducting .Net training, and sweating the truth out of job candidates who claimed to have .Net experience (You'd be amazed at the number of Silicon Valley engineers who list "three years of C#" on their resumes, who are unable to explain how to hook up a simple event handler, or identify basic terms like "reflection", "attributes" -- or "Richter" and "Löwy").

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberiman khalil27 Aug '12 - 5:57 
nice, Short, efficient
QuestionExcellent postmemberpatelsachin13 Dec '11 - 3:13 
short and sweet implementation
GeneralMy vote of 1member__Lj__28 Sep '10 - 4:12 
NI
GeneralHere's another no-code version, supports images too!memberawcullen214 Oct '08 - 4:36 
Add style to Application.Resources (or your ResourceDictionary)
 
<pre>           
 
<!-- Examples
 
                  <TextBox x:Name="txtCustomerName"
                              Style="{StaticResource WatermarkTextBox}"
                              Tag="Enter Customer Name" />
           
                  <TextBox x:Name="txtSearchText"
                              Style="{StaticResource WatermarkTextBox}">
                        <TextBox.Tag>
                              <Image Source="Resources/find_16.png"
                                       Stretch="None"
                                       HorizontalAlignment="Right" />
                        </TextBox.Tag>
                  </TextBox>
 
            -->
            <Style x:Key="WatermarkTextBox"
                     xmlns:theme="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
                     TargetType="{x:Type TextBox}"
                     BasedOn="{x:Null}">
                  <Style.Resources>
                        <LinearGradientBrush x:Key="TextBoxBorder"
                                                      StartPoint="0,0"
                                                      EndPoint="0,20"
                                                      MappingMode="Absolute">
                              <LinearGradientBrush.GradientStops>
                                    <GradientStop Color="#ABADB3"
                                                         Offset="0.05" />
                                    <GradientStop Color="#E2E3EA"
                                                         Offset="0.07" />
                                    <GradientStop Color="#E3E9EF"
                                                         Offset="1" />
                              </LinearGradientBrush.GradientStops>
                        </LinearGradientBrush>
                  </Style.Resources>
                  <Setter Property="Foreground"
                              Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
                  <Setter Property="Background"
                              Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" />
                  <Setter Property="BorderBrush"
                              Value="{StaticResource TextBoxBorder}" />
                  <Setter Property="BorderThickness"
                              Value="1" />
                  <Setter Property="Padding"
                              Value="1" />
                  <Setter Property="AllowDrop"
                              Value="true" />
                  <Setter Property="FocusVisualStyle"
                              Value="{x:Null}" />
                  <Setter Property="Template">
                        <Setter.Value>
                              <ControlTemplate TargetType="{x:Type TextBox}">
                                    <theme:ListBoxChrome x:Name="Bd"
                                                                  BorderThickness="{TemplateBinding BorderThickness}"
                                                                  BorderBrush="{TemplateBinding BorderBrush}"
                                                                  Background="{TemplateBinding Background}"
                                                                  RenderMouseOver="{TemplateBinding IsMouseOver}"
                                                                  RenderFocused="{TemplateBinding IsKeyboardFocusWithin}"
                                                                  SnapsToDevicePixels="true">
                                          <Grid>
                                                <ScrollViewer x:Name="PART_ContentHost"
                                                                     SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                                                <ContentPresenter x:Name="Watermark"
                                                               Opacity="0.5"
                                                               Margin="3,1"
                                                               Content="{TemplateBinding Tag}"
                                                               VerticalAlignment="Center"
                                                               Visibility="Hidden" />
                                          </Grid>
                                    </theme:ListBoxChrome>
                                    <ControlTemplate.Triggers>
                                          <Trigger Property="IsEnabled"
                                                      Value="false">
                                                <Setter TargetName="Bd"
                                                            Property="Background"
                                                            Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
                                                <Setter Property="Foreground"
                                                            Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                                          </Trigger>
                                          <MultiTrigger>
                                                <MultiTrigger.Conditions>
                                                      <Condition Property="Text"
                                                                     Value="" />
                                                      <Condition Property="IsKeyboardFocusWithin"
                                                                     Value="false" />
                                                </MultiTrigger.Conditions>
                                                <Setter TargetName="Watermark"
                                                            Property="Visibility"
                                                            Value="Visible" />
                                          </MultiTrigger>
                                    </ControlTemplate.Triggers>
                              </ControlTemplate>
                        </Setter.Value>
                  </Setter>
            </Style>
</pre>
 
Andy
GeneralSimply greatmemberparxs24 Jun '08 - 5:09 
This is a great example of simple xaml code replacing lines of code-behind. As far as excessive use of TextBlocks as mentioned by one commenter, I agree that this can be a resource issue, but most code-behind solutions that I have seen for this problem (including my own Sigh | :sigh: ) have used a paired TextBlock as well. The code download includes an expansion on the original idea (using multi-value converter) which I think is worth discussion in the main description.
<soapbox>
A lot of developers do not realize the power of xaml with converters. Combine that flexibility with a view model controller and you *almost* remove the need for often messy code-behind - it moves one step closer to the panacea of ui-code separation.
</soapbox>
 
Thanks for sharing this little bit of knowledge!
GeneralEasy solution, but think about efficiencymemberDusan Kocurek17 Jun '08 - 8:45 
Hi,
 
your solution is maybe simple and elegant, but you have to think about performance. If your application will have more complex UI, every text box is doubled and this can have big impact for WPF performance.
 
Please consider, that text box is more complex object (look at visual tree). In our application we used text block massively, but later we reduced theirs number. Impact of such step was visible immediately.
 
Regards
GeneralRe: Easy solution, but think about efficiencymvpJosh Smith26 Jul '08 - 9:43 
Dusan Kocurek wrote:
If your application will have more complex UI, every text box is doubled and this can have big impact for WPF performance.

 
Not really. TextBlocks are very lightweight. If you snoop or mole a WPF element tree, you'll see that there are thousands of elements in a moderately complex view. I seriously doubt that adding another TextBlock for each TextBox will be the feather that breaks the camel's back.
 
:josh:
My WPF Blog[^]
All of life is just a big rambling blog post.

GeneralPerfect Timingmemberjackmos17 Jun '08 - 3:54 
This is exactly what I need for a project and I was wrestling with how to do it. Your solution is so simple and elegant. 5 stars.
GeneralGood onememberUro15 Jun '08 - 22:48 
I agree, we all need to get out of our mind boxes.
 
You showed a nice example. Keep going Smile | :)

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 26 Jul 2008
Article Copyright 2008 by AndyL2
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid