Share via


Exercise 5: RichTextBox

One way we could enhance user registration would be to enable users to supply some sort of text about themselves—if we wanted to add some sort of forum system to the site, the ability to provide a more detailed online identity would be important. We could add a simple text box to hold such information, but that’s a little limited. It might be better to use the RichTextBox added in Silverlight 4, which provides a way to display and edit formatted text—unlike the plain TextBox, the edited text can contain a mixture of plain, bold, or italic text, with different font sizes, colors, and typefaces if you choose. And the RichTextBox offers a read-only mode enabling such formatted text to be displayed easily. (It was possible to render formatted text in earlier versions of Silverlight, but the RichTextBox makes it much easier.)

In fact we could use this feature in other places. For example, the event description itself is currently just plain text, but could be enhanced with the use of more flexibly styled text. Since the RichTextBox is very broadly applicable, this part of the lab will look at it in isolation, rather than wiring it into a specific part of the example application, so that we can focus on the details of using the RichTextBox itself.

  1. Open Visual Studio 2010.

In Visual Studio 2010, press Ctrl+Shift+N, or use the File→New→Project... menu item.

  1. Select Visual C#→Silverlight(C#) or Visual Basic→Silverlight(VB) in the treeview on the left.
  2. In the center, select the Silverlight Application template.
  3. Name the project RichTextApp.
  4. C#:

Visual Basic:

Figure 2

Add New Silverlight Application Project

  1. This project does not need a web site, so uncheck the “Create directory for solution” checkbox.
  2. Click OK.
  3. In the New Silverlight Application dialog that appears, uncheck the checkbox labeled Host the Silverlight application in a new Web site. Click OK.
  4. In the MainPage.xaml file that appears in Visual Studio, add the following inside the Grid:

    XAML

  5. Run the application. The empty rich text box will be filling the whole browser frame. Click in it and try typing something—at this stage it will behave in the same way as an ordinary TextBox. (It does not automatically handle common shortcuts such as Ctrl+B, so at this stage, you can’t exploit the ‘rich’ in rich text box.)
  6. A RichTextBox can be pre-loaded with formatted content. Inside the <RichTextBox> element, add the following XAML:

    XAML

  7. Run the application. You will see a mixture of normal and bold text. You can edit the text, and whatever text you type will be in the same style as the surrounding text.
    Note:
    The design surface in the Release Candidate of Visual Studio 2010 is not completely consistent with what you’ll see at runtime. It handles whitespace differently. If you look closely, you’ll see that in Visual Studio, there is no space between the period at the end of the first sentence, and the start of the next sentence. But when you run the application, there is a space.

    The rules for whitespace handling in XAML are somewhat complex, but they are designed to provide the results you are most likely to want most of the time while letting you indent your XAML to reflect its structure. (They are similar in spirit to HTML’s whitespace handling.) For example, although both lines containing text in the XAML are indented, those indentation spaces do not appear at runtime. A run of whitespace in the middle of mixed content typically collapses down to a single space—so although the XAML has a new line, and space for indentation before the <Bold> tag, at runtime this is reduced to one space, which is what we want here. Whitespace at the beginning and end is handled differently—the whitespace between the opening <Paragraph> tag and the first bit of text is collapsed down to nothing, as is the whitespace between the closing </Bold> and the closing </Paragraph> tag.

    Although the whitespace handling usually does what you what, it’s occasionally necessary to take more control, so the next few steps will show the two ways to do this in XAML.

Whitespace Handling

  1. In the XAML, add a couple of extra spaces between the words “is” and “some” on the first line of text. Notice that this has no effect, either in the designer or when you run the application.
  2. Add an xml:space="preserve" attribute to the opening <Paragraph> tag. The extra spaces you added will now be faithfully reproduced. So will any new lines—the two pieces of text new appear on two separate lines. More subtly, there’s a blank line at the start, because the opening <Paragraph> tag is followed immediately by a new line, and that has also been preserved. Moreover, the indentation has carefully been maintained too, which is not especially useful here.
    Note:
    The xml:space attribute is a standard feature of XML, and you can add it to any XML document. The XML specification deliberately leaves some latitude for how applications use this attribute, and XAML’s whitespace rules explicitly take this attribute into account. So while this is a standard attribute, there is some XAML-specific behavior here.
  3. Remove the xml:space attribute you just added.
  4. Inside the <Paragraph> element, replace the first line of text with this:

    XAML

  5. Note that these extra spaces are honored for this particular run of text, but the other whitespace is collapsed as before. This is the usual technique for taking fine-grained control of whitespace when you only need precise control in a specific area.
    Note:
    This works because the whitespace processing rules are applied to plain text when it appears as element content, but not to text inside attribute values.

    Be aware that if you don’t specify a Run explicitly as we just have, the XAML compiler converts plain textual content into Run elements for you—Run is the only type in the RichTextBox text object model that is capable of containing text. If you look at the properties offered by types such as Paragraph or Bold, you’ll see that that the only things these elements are capable of containing are other text elements. Only Run has a Text property. So you will end up with Run elements whether or not you put them explicitly in the XAML.

Nested Formatting

  1. Formatting tags can be nested within one another, and their settings combine. And you can also apply text formatting attributes to the Paragraph container. Copy this XAML inside your RichTextBox to see this in action:

    XAML

    Note:
    The <Span> element here is similar to <Bold> and <Italic> in that it can be used to apply formatting to a range of text. In fact the Bold and Italic classes both derive from Span. The difference is that Span has no intrinsic formatting of its own—it has no visible effect unless you set one or more font properties. The Bold element is essentially just a Span that sets the FontWeight to Bold for you.

    Because Span supports the FontWeight and FontStyle properties, you can use a Span to do anything you could do with Bold or Italic. Those elements are only provided for convenience.

Read-only RichTextBox

  1. Add an IsReadOnly="True" attribute to the RichTextBox element. Run the application again. Observe that the text is displayed but can no longer be edited. Instead, it can be selected and copied.
    Note:
    This mode is useful for displaying formatted text. If you are familiar with WPF, you would normally use one of the flow document reader controls for this purpose, but Silverlight combines editing and viewing functionality into the one control, RichTextBox.
  2. When using a RichTextBox in this way, it is common to want to support scrolling to show more text than will fit. This is built in. Add a VerticalScrollBarVisibility="Visible" attribute to the RichTextBox, and add some more paragraphs to the content so that you have enough text to make a scrollbar necessary. Run the application to verify that the scrollbar works as you’d expect.
  3. When displaying large volumes of text, you will typically want text wrapping enabled. Add a TextWrapping="Wrap" property to the RichTextBox.

Programming the RichTextBox

  1. To enable the end user to apply formatting, we need to provide some additional UI and code. Add the following XAML at the top of the Grid (before the RichTextBox).

    XAML

    <StackPanel Orientation="Horizontal" Grid.Row="1"> <Button x:Name="boldButton" Content="Bold" /> <Button x:Name="italicButton" Content="Italic" /> </StackPanel>

  2. Add Click event handlers to both buttons.
  3. In the code behind, add this helper:

    C#

    private void ToggleSetting(DependencyProperty property, object onValue, object offValue) { object currentValue = rtb.Selection.GetPropertyValue(property); bool alreadyApplied = currentValue.ToString() == onValue.ToString();

    rtb.Selection.ApplyPropertyValue(property,
                                     alreadyApplied ? offValue : onValue);
    

    }

    Visual Basic

    Private Sub ToggleSetting(ByVal [property] As DependencyProperty, ByVal onValue As Object, ByVal offValue As Object) Dim currentValue As Object = rtb.Selection.GetPropertyValue([property]) Dim alreadyApplied As Boolean = currentValue.ToString() = onValue.ToString()

    rtb.Selection.ApplyPropertyValue([property],If(alreadyApplied, offValue, onValue)) End Sub

    Note:
    This detects whether a particular property is already applied across the current selection in the RichTextBox. If it is not already present, then that property is applied. If it is already present, then the ‘off’ value is applied instead. (We cannot just remove the property. For one thing, the selection object does not offer a method for doing that, but in any case, the property may be inherited from a containing element. For example, the entire Paragraph might be bold, but we might want to unbold some selected text. So the absence of a property on the selection is not enough—we need to explicitly apply a property forcing the property back to normal to be able to override formatting applied at a containing scope.
  4. Implement the bold button’s click handler like this:

    C#

    private void boldButton_Click(object sender, RoutedEventArgs e) { ToggleSetting(TextElement.FontWeightProperty, FontWeights.Bold, FontWeights.Normal); }

    Visual Basic

    Private Sub boldButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) ToggleSetting(TextElement.FontWeightProperty, FontWeights.Bold, FontWeights.Normal) End Sub
  5. Similarly, implement the italic button like this:

    C#

    private void italicButton_Click(object sender, RoutedEventArgs e) { ToggleSetting(TextElement.FontStyleProperty, FontStyles.Italic, FontStyles.Normal); }

    Visual Basic

    Private Sub italicButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) ToggleSetting(TextElement.FontStyleProperty, FontStyles.Italic, FontStyles.Normal) End Sub
  6. In the XAML, remove the IsReadOnly attribute from the RichTextBox.
  7. Run the application, select some text in the RichTextBox, and verify that the Bold and Italic buttons toggle the bold and italic appearance of the text.
  8. Finally, the ability to edit formatted text wouldn’t be much use if we couldn’t get hold of the current contents, so we’ll see how to retrieve the formatted text. The RichTextBox makes this available as XAML. Add the following to the XAML, after the Grid.RowDefinitions element, but before the StackPanel:

    XAML

    <Grid.ColumnDefinitions> <ColumnDefinition Width="" /> <ColumnDefinition Width="" /> </Grid.ColumnDefinitions>

    <ScrollViewer Grid.Column="1" Grid.RowSpan="2"> <TextBlock x:Name="textAsXaml" TextWrapping="Wrap" /> </ScrollViewer>

  9. Add a handler for the RichTextBox element’s ContentChanged event. Implement it as follows:

    C#

    private void rtb_ContentChanged(object sender, ContentChangedEventArgs e) { textAsXaml.Text = rtb.Xaml; }

    Visual Basic

    Private Sub rtb_ContentChanged(ByVal sender As Object, ByVal e As ContentChangedEventArgs) textAsXaml.Text = rtb.Xaml End Sub
  10. Run the application, and make a modification to the contents of the RichTextBox. The right-hand side of the UI should show the Xaml for the RichTextBox’s current contents.