Monday, February 9, 2009

WPF Watermark TextBox using XAML Style

What is watermarked TextBox



Watermarked TextBox is a special type of text input control which displays a message or image when it is empty.Usually the message will be "Enter the text" and sometimes it may be description about that data field.



When we click on the TextBox ie getting focus the watermark text ("Enter the text") will disappear and it will become an ordinary TextBox.



Developing Watermark TextBox



The tasks of developer are hiding the watermark on getting focus and showing again on lost focus, if the user doesn't entered value in that.ie if the TextBox is empty watermark should come again.



In earlier days this itself was a control which is inherited from normal TextBox.But with the help of Templating and Trigger support in WPF we could achieve it very easily without inheriting.Just by Stying and Templating an ordinary TextBox.



Structure of Watermark TextBox Style



The style can be based on the style of TextBox itself.This reduces our effort in implementing normal TextBox behaviors.We are just going to concentrate on Template.



The normal TextBox template contains a ScrollViewer to hold the data.We are just going to put a TextBlock over that which is going to hold the Watermark text("Enter the Text").By default the visibility of this new TextBlock will be hidden.



Inside ControlTemplate





<Grid>


    <ScrollViewer x:Name="PART_ContentHost"


                  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />


    <TextBlock x:Name="textBlock"


               Opacity="0.345"


               Text="Enter Text Here"


               TextWrapping="Wrap"


               Visibility="Hidden" />


</Grid>





Now we write Trigger to  show this TextBlock on demand.There are two conditions which shows this watermark.They are "IsFocused=False" and Length of the Text=0.So we write a MultiTrigger with 2 conditions in it.One is for LostFocus and other is for Text.Length respectively.





<ControlTemplate.Triggers>


    <MultiTrigger>


        <MultiTrigger.Conditions>


            <Condition Property="IsFocused"


                       Value="False" />


            <Condition Property="Text"


                       Value="" />


        </MultiTrigger.Conditions>


        <Setter Property="Visibility"


                TargetName="textBlock"


                Value="Visible" />


    </MultiTrigger>


</ControlTemplate.Triggers>




Now whenever the TextBox comes in a state where the IsFocused=False and Text="" the Watermark TextBlock will get displayed.You can replace the watermark TextBlock by any image or what ever you want..



Complete Style





<Style x:Key="WaterMarkTextBoxStyle"


       BasedOn="{StaticResource {x:Type TextBox}}"


       TargetType="{x:Type TextBox}">


    <Setter Property="Template">


        <Setter.Value>


            <ControlTemplate TargetType="{x:Type TextBox}">


                <Grid>


                    <ScrollViewer x:Name="PART_ContentHost"


                                  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />


                    <TextBlock x:Name="textBlock"


                               Opacity="0.345"


                               Text="Enter Text Here"


                               TextWrapping="Wrap"


                               Visibility="Hidden" />


                </Grid>


                <ControlTemplate.Triggers>


                    <MultiTrigger>


                        <MultiTrigger.Conditions>


                            <Condition Property="IsFocused"


                                       Value="False" />


                            <Condition Property="Text"


                                       Value="" />


                        </MultiTrigger.Conditions>


                        <Setter Property="Visibility"


                                TargetName="textBlock"


                                Value="Visible" />


                    </MultiTrigger>


                </ControlTemplate.Triggers>


            </ControlTemplate>


        </Setter.Value>


    </Setter>


</Style>





This can be done by a graphics designer alone.That means using Expression Blend.Will be posting steps later.


16 comments:

  1. At www.xamltemplates.net you can find themes for all the wpf controls, check it out and on the blog i have an article with the vista login screen which uses a watermark

    ReplyDelete
  2. Thanks! That was helpful.

    The original TextBox template has a border, that is missing in your template. To get that border to display, I've place a Border around the Grid in the template:

    BorderBrush="#FF7F9DB9", BorderThickness="1"

    ReplyDelete
  3. Happy to know that ,this post has helped you.
    Thanks for your suggestions..
    Awaiting more suggestions

    ReplyDelete
  4. Great work. I've added a little bit to the control template by using the ClassicBorderDecorator so that it get's that default textbox look.

    From there, I've been trying to make this into a custom control so that the watermark text can be set per on a per control basis. I've been trying to simply extend textbox, add a dependency object that is the watermark text, and replace the content template, but I keep losing all of the base textbox functionality when I override the framework metadata.

    ReplyDelete
  5. Hi Matthew,

    That is a nice idea to have a DP to set the watermark text.But I didn't get why you override framework metadata to create new DP.Anyway I will be writing a new post about creating Custom WatermarkTextBox by inheriting TextBox.

    ReplyDelete
  6. Hi Joymon,

    This has been very helpful. I am trying to modify your XAML to include one more bit of functionality. I would like to change the background color of the TextBox (not the TextBlock) when it has focus. I tried the following, but its not working. Any help would be greatly appreciated.

    Regards,
    Sujoy

    I added a Trigger block after the Multi-trigger block:


    <Style x:Key="WaterMarkTextBoxStyle" BasedOn="{StaticResource {x:Type TextBox}}" TargetType="{x:Type TextBox}">
    <Setter Property="Template">
    <Setter.Value>
    <ControlTemplate TargetType="{x:Type TextBox}">
    <Border BorderBrush="#FF7F9DB9" BorderThickness="1" CornerRadius="5">
    <Grid>
    <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
    <TextBlock x:Name="textBlock" Opacity="0.345" Text="Enter Text Here" TextWrapping="Wrap" Visibility="Hidden" VerticalAlignment="Center" HorizontalAlignment="Left" Padding="3"/>
    </Grid>
    </Border>
    <ControlTemplate.Triggers>
    <MultiTrigger>
    <MultiTrigger.Conditions>
    <Condition Property="IsFocused" Value="False" />
    <Condition Property="Text" Value="" />
    </MultiTrigger.Conditions>
    <Setter Property="Visibility" TargetName="textBlock" Value="Visible" />
    </MultiTrigger>
    <Trigger Property="IsFocused" Value="True">
    <Setter Property="Background" Value="LightBlue"/>
    </Trigger>
    </ControlTemplate.Triggers>
    </ControlTemplate>
    </Setter.Value>
    </Setter>
    </Style>

    ReplyDelete
  7. A quick suggestion.
    use the targetname property..


    <Setter Property="Background" Value="LightBlue" TargetName="PART_ContentHost"/>


    This is not tested since I dont have the provision now.

    ReplyDelete
  8. Thanks for your article.
    I have one requirement for watermark diangle to image printing in wpf. can you help me to create watermark image on original Image?

    ReplyDelete
  9. If my understanding about your requirement is rite,you can print the watermark by getting the visual of the watermark textbox.It will include the watermark in the control.

    Let me know if you are thinking differently

    ReplyDelete
  10. when using Border around grid it's better to use TemplateBinding:

    ReplyDelete
  11. Hi Joymon, thank you for watermark textbox. Is it possible to set watertext dynamically in the xaml code. For exmple I want to create two watermark textboxes. The First watermark text will be "First Name" and the second one will be "Last Name"

    ReplyDelete
  12. Yes Armenius ..We can do it.Just create a property for Watermark and bind the watermark textblock to that. Something similar to how the HeaderedContentControl works.

    ReplyDelete
  13. Great, helped me out in a pinch! Thank you

    ReplyDelete
  14. Hi Anony,

    If you have a sample ,pls send to me so that I can check what went wrong?

    joymon@gmail.com

    Joy

    ReplyDelete