Showing posts with label TextBox. Show all posts
Showing posts with label TextBox. Show all posts

Wednesday, June 30, 2010

Custom Controls – Extending the TextBox

The good folks over at Silverlight Show have just put up my article about writing a custom control. One of my earliest posts was on using a UserControl to create a WatermarkedTextBox. I’ve been meaning to follow up on it for a while on how to achieve the same thing (but better) using a Custom Control. In the example below it’s wired up as a filter for the list box. You can check the “Styled” box to apply a different style to the whole app, including the WatermarkedTextBox.

Go over to Silverlight Show to grab the source and read the full article.

Tuesday, March 2, 2010

Watermarked TextBox Part II

In the first post we created the visual elements for our WatermarkedTextbox user control. In this post we are going to add some code, create a few more states, and wire everything up.

Remember that you can grab the finished control from here.

You can view a working sample on the Expression Gallery here.

Before we add any code-behind, we'll create two state groups - one for showing or hiding the watermark, the other for showing or hiding the clear-text button.

Select the States tab and click the Add State Group button. Name the new state group "ClearFilterButtonStates", since this group is going to hold the states for the ClearFilterButton.

Before we add a state for the button, lets set the default appearance of the ClearFilterButton. Since the default text in the TextBox will be an empty string, we don't need to show the ClearFilterButton until there is some text to clear, so set the Opacity of the ClearFilterButton to 0. I want the button to animate as it appears; expanding from a dot until it reaches it's full size. To make the ClearFilterButton start out as a dot, go to the RenderTransform group on the Properties panel, click on the Scale tab and set the X and Y values to 0 as shown here:


Now click the Add State button on the ClearFilterButtonStates line and name the new state "ButtonVisible". Add another state named "ButtonHidden".

Select the "ButtonVisible" state (Blend will show the red border to indicate it is recording property changes for the selected state) and then select the ClearFilterButton. Set it's opacity to 100, and it's X and Y scale render transform values back to 1. We want the button to animate, rather than appear all at once, so let's change the transition time value for the whole ClearFilterButtonStates to 0.3:


Click on the "Turn on transition preview" button in the top right of the States panel, select the [UserControl] element in the Objects tree view and click back and forth between the ButtonVisible state and the ButtonHidden state.

Select the "Base" state group so any changes we make next are not just recorded for a state. The button is a little to close to the edges, so lets give it some room by setting it's Right, Top, and Bottom margin to 3.

Now we will create the states for the watermark in much the same way. Create a new state group called "WatermarkStates" and add two states called "WatermarkVisible" and "WatermarkHidden". We don't need to do anything for the WatermarkVisible state, so just select the WatermarkHidden state, select the WatermarkText control and set it's Opacity to 0. Also, expand the Common Properties group on the Properties panel and turn off "IsHitTestVisible" - we don't want it interfering with the text box interaction.

We want the watermark to disappear straight away when the user clicks in the text box, so we won't set a group level transition time for the Watermark States, but it would be nice to have it fade in when the text box becomes empty. Click the Add transition button on the WatermarkHidden state and select the "WatermarkHidden -> WatermarkVisible" option:


And set the transition time to 0.4:


Now we are ready to add some code to put make the whole control work.

This post has already gone longer than I thought it would, so I'm going to describe the code in Part III.

Monday, March 1, 2010

Watermarked TextBox Part I

The project I'm currently working on has several views that contain lists of things in a DataGrid. We have a tool bar above each table in which we placed a label (that said "Filter : ") followed by a text box for entering text that would cause the list of items to be filtered to only those items containing the filter text, in one of a couple of the displayed columns.

That extra label was taking up some precious screen real estate so it had to go. What we really wanted was a TextBox with a watermark that disappears when it get's the focus or when it contains text. It would look like this when it is empty:

Because it is a filter text box I want a quick and easy way to clear it too. I don't want to have to click in it, select the text, and press the delete key just to clear it. So I want to add a button inside the text box that clears the text when the button is clicked. But I only want the button to appear when there is text inside the text box. It would look like this with text in it:


There are two ways to go about this: Create a UserControl, or create a templated custom control. In this post, and the next, we will create a UserControl since it is the easiest. I want to come back another day to do the templated custom control

Part I
In this first post we will create all the visual elements. The second post will be about creating some properties for binding, and the code to support those properties. The final result can be downloaded from here.

Start a new project in Blend. Select the "Silverlight 3 Application + Website" template. With your new solution, right click the first project in the solution and select "Add New Item...". Select "UserControl" if it's not already selected, give it the name "WatermarkedTextbox" and click OK.

In the Objects and Timeline panel, select the "UserControl" item at the top of the tree view. By default Blend starts you off with a design time size of 640 by 480. To set this to something more practical for this control (200 by 24 should be fine) just grab the design-time resize handle and drag it towards the top left corner of the box - you should see the Width and Height properties in the Properties panel change as you drag it. Stop dragging when the Width and Height end up at 200 and 24 respectively.

First though, we need to add some columns to the LayoutRoot grid. Select the "LayoutRoot" grid in the Objects and Timeline panel, and in the Properties panel, click on the expand button at the bottom of the Layout group.

This will show some more layout properties including "ColumnDefinitions (Collection)". Click the button with the ellipses next to this property to bring up the ColumnDefinitionsCollection Editor.

Use the editor to add two columns. Leave the width of the first column (it should be set to 1 Star), but set the width of the second column to 17 pixels. That second column is going to hold our button for clearing the text from the TextBox.

So now with our two columns in place we are ready to add our controls.

Select the TextBox icon from the text controls flyout on the Blend Tools toolbar (usually on the left of the screen). You can get this flyout by clicking and holding the displayed text control icon as shown in the picture.

Once it's selected, double click it. This will add a new TextBox control to the Layout grid since we had the grid selected when we double clicked. When the TextBox appears Blend swaps into edit mode and has the text "TextBox" already selected and ready for editing in the new TextBox control. Just hit escape for now to leave edit mode. We'll come back later and change that.

Now, with the TextBox control selected in the Objects tree, use the Properties panel to change the default value of HorizontalAlignment to "Stretch", and ColumnSpan to 2. You should end up with a design surface that looks like this:


Now we want to add our watermark. Select the "Layout" grid again, select the "TextBlock" control from the text flyout, then double click the TextBlock icon to create a TextBlock in the grid. Again, Blend will enter edit mode for the TextBlock control so just click escape to exit - we'll come back to this control later.

Blend will have automatically set some values in one or more of the Margin property values for the new TextBlock. Set all of the margins back to 0 except for the left margin - set that to 3, and change the VerticalAlignment to Center. We want the appearance of the watermark to be a little different than the TextBox text, so in the Text group on the Properties panel, click the italics button on, and up in the Brushes group set the Foreground color to something like #FF868686.

Now we can create the button to clear the text from the TextBox. We are going to make it out of two controls; an ellipse and an "x" that we convert to a path. First, select the LayoutRoot grid again, then select the ellipse from the shape flyout on the Tools toolbar and double click it to create a new ellipse. Again, reset all the margins back to 0. Change the Column property to 1, the Width to 14, the Height to 14, the Fill Brush to #FF787878, and the Stroke Brush to #FF494949.

Select the LayoutRoot grid again and add a TextBlock. Change the text of the new TextBlock to lowercase "x" (remember that to exit the text editing mode press escape, not enter) and set the font size to 8. Right click the TextBlock and select "Path" -> "Convert to Path" to convert the text into a path shape. Set all the margins to 0, the Width and Height both to 5, the Horizontal and Vertical Alignments both to Center, the Column to 1, and the Fill color to white.

If you select the LayoutRoot grid again, you should have something like this:


Now select both the Ellipse and the Path in the Object tree (hold Ctrl down to select multiple items). Right click the selected controls and select "Group Into" -> "Grid", to wrap the two controls into a grid. Doing this will have set some default Margin values on the grid so reset them all back to 0. Now right click the new grid and select "Make Into Control...". The Make Into Control dialog will appear with a list of controls to choose from. Select the Button control and give it the name "ClearFilterButtonStyle" and click OK to turn it into a button style.

Blend has now put us into the template of the button style. The top left of the design surface shows three connected boxes showing us where we are editing. The button style is essentially a collection of property values that can be applied to a button. One of those properties is called "Template" which accepts a ControlTemplate. Blend has turned our grid containing the ellipse and path into a ControlTemplate for the button style. Part of that style includes a collection of states, which allow us to change the appearance of parts of our template as things happen to the button, such as when the mouse rolls over it. Don't worry about the "Button" text that has appeared, we will clean that up soon.

If it's not already showing, select the "States" tab. We want to change the appearance of our button to make it interact nicely with the user. Select the "MouseOver" state. A red border will appear around the design surface letting you know that Blend is recording property changes that are only applied when the control is in the MouseOver state. Now select the ellipse and change it's Fill color to #FFBD7777 and it's Stroke color to #FFD63030.

Right click the MouseOver state label and select "Copy State To" -> "Pressed". Select the "Pressed" state and change the path's Fill color to #FFD63030.

To exit editing the template, click the [Button] connected panel at the top left of the design surface, or click the "Return scope to [UserControl]" button at the very top of the Object tree panel. Now we are back editing the user control, clear the text from the Content property of the new button.

To finish up this post, let's name a couple of the controls. If you select a control in the Objects tree, and then single click it's name you can edit it. Change the TextBox name to "TextEntry", the TextBlock name to "WatermarkText", and the button to "ClearFilterButton".

In the next post, we will add some code in the code-behind file to make this user control actually do something.

You can jump ahead and grab the complete solution here if you want to.