A long while back, I wrote an Ajax InPlaceEdit behavior that you could attach to input controls to enable in-place editing experiences in HTML. I was thinking of doing the same in Silverlight using the behavior framework in Silverlight.FX that I used to demonstrate adding auto-complete, text filtering and other functionality to a regular TextBox control.
Here is a scenario - an image with a title (ala Flickr). The title needs to be editable, but it would be suboptimal to show it as a TextBox (since it is not changed frequently). It is also suboptimal to go into a different page or different mode to edit it as that is one step too many. This is where the in-place edit comes into use. An example of this style of ux is in the screenshot below.

In HTML, an in-place edit textbox is simulated using a label in addition to the original input control, and hacking a bit to correctly overlay them and toggling visibility via script event handlers. One of the key strengths and capabilities of the Silverlight presentation framework and XAML is the fact that you can restyle and redefine the visual structure of any control including the intrinsic controls, such as the out-of-the-box TextBox, in a declarative (yet reusable) manner. So I was wondering how far I could get using just styling and templating, which goes far beyond CSS, as a first step without creating a behavior programmatically. Turns out you can actually go quite a ways.
If you haven't played with control styles and templates, the visual state manager feature or Expression Blend yet, I'd recommend reading this tutorial on the general flow for customizing Silverlight controls. I'll walk through the couple of edits I had to make to the default TextBox template to enable in-place editing in the remainder of the post.
I started by dropping a TextBox control onto the design surface in Blend. This is the XAML I am starting with.
<TextBox Text="Turtle" />
Then I right-clicked on the TextBox and chose to edit the control's template, and named the new style as "InPlaceEditTextBox". The resulting xaml contains a copy of the default TextBox template in the Resources section (so it can be reused), and the TextBox element is updated to reference it as follows:
<TextBox Text="Turtle" Style="{StaticResource InPlaceEditTextBox}" />
Now, its time to edit the default TextBox template to provide the in-place editing interaction as shown in the screenshots above.
Step 1 - Border Treatments
The first step is to get the border-less normal look. For that I will make the default border transparent when the TextBox does not have focus.
<Style x:Key="InPlaceEditTextBox" TargetType="TextBox">
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Grid x:Name="RootElement">
<vsm:VisualStateManager.VisualStateGroups>
...
<vsm:VisualStateGroup x:Name="FocusStates">
<vsm:VisualState x:Name="Focused"> ... </vsm:VisualState>
<vsm:VisualState x:Name="Unfocused">
<Storyboard>
...
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<SolidColorBrush Color="Transparent" />
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
<Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" ...>
...
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
What I have done is in a declarative manner, state that whenever the control goes into unfocused state, the TextBox border should become transparent. This gives the appearance of being a regular non-editable label.
Step 2 - Edit Glyph and Background Highlighting on Hover
Now the next step I want to implement is the hover appearance. For this I want to add an edit glyph, and I want to change the background. So I am going to add an image into the visual tree making up a TextBox, and add an animation to the storyboard associated with the mouse over visual state.
<Style x:Key="InPlaceEditTextBox" TargetType="TextBox">
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Grid x:Name="RootElement">
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="CommonStates">
...
<vsm:VisualState x:Name="MouseOver">
<Storyboard>
...
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="MouseOverElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<SolidColorBrush Color="#80FFFFE0" />
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="EditGlyph"
Storyboard.TargetProperty="Opacity">
<SplineDoubleKeyFrame KeyTime="0" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
...
</vsm:VisualStateGroup>
...
</vsm:VisualStateManager.VisualStateGroups>
<Border x:Name="Border" ...>
<Grid>
<Border x:Name="ReadOnlyVisualElement" Opacity="0" Background="#72F7F7F7"/>
<Border x:Name="MouseOverElement" BorderThickness="1">
<Border.BorderBrush>
<SolidColorBrush Color="Transparent" x:Name="MouseOverColor"/>
</Border.BorderBrush>
<ScrollViewer ... x:Name="ContentElement"/>
</Border>
<Image x:Name="EditGlyph" Source="Pencil.png"
Opacity="0" Margin="0,0,4,0" Height="11" Width="12"
HorizontalAlignment="Right" VerticalAlignment="Center"
ToolTipService.ToolTip="Click to Edit" />
</Grid>
</Border>
...
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Effectively what I can do is change the visual structure of the element without changing its core TextBox behavior encapsulated in the class representing the TextBox. This lets me add the image without interfering with default functionality.
Those are pretty much the only two edits I had to make! Now I can have as many TextBoxes as I want referencing the style and template I just created.
You can download the little sample project which contains the full definition of the style to open up in Blend and play with it further. Eventually I might still build an in-place edit behavior. What the behavior will let me do is base the visualization of the input control on a combination of factors such as validation state in addition to hover and focus. However for basic scenarios re-defining control templates does provide some compelling techniques that are declarative, more toolable and and designer-friendly.
Side note - as I was putting together this blog post, I noticed Bertrand posted an updated Ajax in-place edit behavior implementation as well. Check it out if you're interested on the Ajax side of things as well.
Posted on Saturday, 11/22/2008 @ 11:53 AM
| #
Silverlight