Thursday, March 4, 2010

Watermarked Textbox Part III

This is the third and final post on creating a Watermarked TextBox UserControl. The first two parts can be found here and here. In those posts we created all the visual elements, including the animations we need.

The final version can be downloaded here. I finally worked out the easiest way to embed silverlight apps in the blog so here is the finished working demo app:


In this post we will look at the code we need to make it all work. Although writing code is easier in Visual Studio, we will do it all in Blend since we have been working in there anyway. Be prepared; this post doesn't have much in the way of graphical content - it's pretty much all code.

Dependency Properties
Since we have buried the TextBox control inside a UserControl we need to expose a couple of properties to make this reusable. Although we could do a thorough job and expose quite a few properties of the TextBox control, I just want to expose two properties for now: the Text property and the Style property.

There are plenty of other tutorials on the web about Dependency Properties so if you are not familiar with them then it would pay to do a bit of reading later on; but for now all you need to know is that the we are going to use them to expose properties on our user control.

The Text property

Open up the WatermarkedTextbox.xaml.cs file and you should see the current code behind. The first property to declare looks like this:

This declares a bindable dependency property on our UserControl that another consuming UserControl can use to set the text content of our embeded TextControl. So if you placed the control on another UserControl and set it's Text property, the static method TextChangedHandler will be called for us to respond to the new text value. The TextChangedHandler method looks like this:

The above static method ensures that the change to our UserControl's text property gets passed on to our embedded TextBox control. If the new text is not empty then we go to the WatermarkHidden state, and the ButtonVisible state. We still have to handle the case where the user types into our embeded TextBox; we need to update our UserControl's Text DependencyProperty. We cant bind the TextEntry.Text property in the XAML against the UserControl's Text property because we would have to give our UserControl an "x:Name=..." attribute, and you can't name UserControls if you want to use more than one instance on the same parent UserControl; otherwise you get a nasty error that stops you Silverlight app dead in it's tracks.

So we are going to handle the TextChanged event on our embeded TextEntry TextBox. To do this, select the TextEntry TextBox control and on the Properties page select the "Events" button in the top left corner. You will see all the events that the TextBox control exposes. Double click the "TextChanged" event and Blend will create a method stub in our code-behind called "TextEntry_TextChanged". The code we need to write here looks like this:
This method does two important things: it updates our UserControl's Text DependencyProperty and it updates the states that the UserControl needs to be in. So now our Text property updates and is updates in sync with the TextEntry TextBox's Text property. Consuming UserControls can use our UserControl's Text property the same way they would have used the TextBox's Text property (including binding).

The Watermark Property

The watermark is a little easier. It's not an editable control so we only have to update it in one direction. The declaration for the DependencyProperty and UserControl property looks like this:
And the WatermarkChangedHandler is as follows:
The TextBoxStyle Property

The last Dependency Property to add is the Style property. This will allow users of our FilteredTextBox UserControl to supply a style to change the appearance of the text box. If we were going to be really thorough we would also provide properties for the button and the watermark too - and there is nothing stopping you from adding that functionality yourself!

Here is the DependencyProperty declaration for the Style property:
Pretty straight forward by now. And you can probably guess what the TextBoxStyleChanged method will look like too:
The Last Events

There are three more events that we need to write code for. Back on the design surface select the TextEntry control and go to the Events page of the Properties panel again. Create an event handler for both the GotFocus and LostFocus events. The code for these events is going to make sure the Watermark disappears and appears at the right time. It requires us to use a flag to keep track of when our TextBox has the focus. The flag declaration and method handlers look like this:
And the last event to handle is the user clicking on our ClearFilterButton. Use the same technique as above to create an event handler for the Click event on our button. The code looks like this:
And we're finished!

The download for this control also has support for Commanding, which will only mean something to you if you are using the MVVM pattern, in which case the extra code will probably be straight forward for you anyway - so I won't go over it here.

Summary

So you now have a working, reusable UserControl that gives you the functionality of a Watermarked TextBox control. Back in Part I of the series I said we could go about building this control two ways: as a UserControl or as a templated custom control. The most flexible way would have been as a templated custom control - and I hope to do a series on this at a later stage - but the easiest way is using a UserControl.

Note : for those of you who have already downloaded the control, I have updated the file on Microsoft Expression Gallery, and the alternate download location. I noticed an incorrect binding I had left on the TextEntry text box, which is now removed.

Feel free to use this control for your own projects - if you like it, please leave a comment below!

Download full source code here or here.

15 comments:

  1. Excuse some of the formatting - I'm still getting to grips with Blogger and with the syntax highlighter.

    ReplyDelete
  2. Cool control Phil - we created one and did it a little differently without using visual states for the Watermark... but I think I like yours better :)

    ReplyDelete
  3. There's a minor bug that should be easy enough to fix. It appears that clicking on the (x) doesn't force focus into the TextBox.

    In your running sample, click into the second textBox to give it focus. Then click on the (x) in the first textbox. Notice that although the text on the first box is cleared as expected, the focus remains in the second textbox.

    ReplyDelete
  4. I guess it could be seen as a bug. In the context that we were using it, the textbox was a filter for a list. Clearing the filter didn't mean that you wanted to start typing again, it just meant that the filter was cleared. So it was actually a feature in our case, not a bug :)

    ReplyDelete
  5. how do we validate data? for example if we entered email address and check whether user entered a valid email or not?

    ReplyDelete
  6. you would have to modify the code in the UserControl to allow the TextBox there to handle validation.

    This control was originally designed as a simple filter box above a data grid, so it didn't need to handle validation. If you require validation then you are best to create a custom control.

    I will hopefully be adding a post soon that shows how to create a watermark textbox as a custom control.

    ReplyDelete
  7. Thanks and looking forward for watermark textbox as a custom control.

    ReplyDelete
  8. Very useful tutorial and control. Thank you for writing this blog post!

    ReplyDelete
  9. Thank you for the kind feedback.

    The tutorial that covers the textbox as a custom control is here

    ReplyDelete
  10. Another thing that must be added to this cool control is to control the text length in the text box so as when a user writes many letters the text must not go under the icon...
    may be adding another transparent textbox over the original text box and it will lay in the 1st column in the grid..and the behind textbox will be for decoration not more and the real text will be in the front transparent textbox..
    I think u understood me..!!
    Sami..

    ReplyDelete
  11. Sami, you are right.

    This was fixed here when I made it a custom control rather than a user control.

    ReplyDelete
  12. Is it possible to edit the text in the design view? More like the regular TextBox.

    I want to group two TextBox'es into a new control but I loose the ability to change the text in design view.

    Everyone keeps saying that UserControl's are perfect for grouping and reusing controls. But they are not worth much if I can't customize them.

    ReplyDelete
  13. Xombul - no, User Controls do not let you edit contained controls at design time.

    You can expose properties of the contained controls up through the user control though. For example, you could create two properties such as Text1 and Text2 on your User Control and bind them to the text properties on your two text boxes.

    Then, at design time, you can edit those properties on the User Control.

    If you just want to edit the text on a single instance of the watermarked text box then use the custom control instead since it behaves like a text box instead of a user control.

    ReplyDelete
  14. I have tried to expose properties in a UserControl - but I am only able to edit them in the "property inspector" and not in the "design view"

    I am wondering if there is a way to expose text properties so that they will be available in the "design view" or "design surface"

    ReplyDelete
  15. Xombul - short answer is "no". Not possible with User Controls.

    ReplyDelete