Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing the font size of a RichTextBox Selection through a ComboBox doesn't work properly

Tags:

c#

events

wpf

xaml

I have created a very simple WPF Window consisting of a Grid containing one RichTextBox and one ComboBox. I use the ComboBox for changing and looking up the font size of the RichTextBox selection.

Here's the code-behind file of my XAML:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        // Add the font sizes.
        for (var i = 1; i < 72; i++)
        {
            FontSize.Items.Add((double) i);
        }
    }

    private void MyTextBox_SelectionChanged(object sender, RoutedEventArgs e)
    {
        // If the selection changes, update the font size in the ComboBox.
        FontSize.SelectedValue = (double) MyTextBox.Selection.GetPropertyValue(TextBlock.FontSizeProperty);
    }

    private void FontSize_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        // If the selected size changes, change the size of the selection in the RichTextBox.
        if (FontSize.SelectedItem != null)
            MyTextBox.Selection.ApplyPropertyValue(TextBlock.FontSizeProperty, FontSize.SelectedItem);
    }
}

There are two things here:

  1. MyTextBox_SelectionChanged updates the ComboBox with the font size of the selection.
  2. FontSize_SelectionChanged changes the font size of the selection.

You can see the problem below:

enter image description here

When I make a selection and change the font size, it changes perfectly. But the moment I click on another text with a different font size it changes back again.

What is causing this behavior?

Edit: Here's the XAML file:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ComboBox x:Name="FontSize" HorizontalAlignment="Left" VerticalAlignment="Top" Width="497" Margin="10,10,0,0" SelectionChanged="FontSize_SelectionChanged"/>
        <RichTextBox x:Name="MyTextBox" HorizontalAlignment="Left" Height="273" VerticalAlignment="Top" Width="497" Margin="10,37,0,0" RenderTransformOrigin="0.358,0.48" SelectionChanged="MyTextBox_SelectionChanged">
            <FlowDocument>
                <Paragraph>
                    <Run Text="RichTextBox"/>
                </Paragraph>
            </FlowDocument>
        </RichTextBox>

    </Grid>
</Window>

Edit 2: Here's the short explanation of what I did when I was debugging it:

  1. There are two debug points, one at MyTextBox_SelectionChanged, and one at FontSize_SelectionChanged.
  2. When I change the font size, I click on F5 and continue.
  3. When I click on another part of the text (that has the default size), MyTextBox_SelectionChanged is called. The Selection.Text is empty.
  4. Then I continue again and stop at the call to FontSize_SelectionChanged. But still the Selection.Text is empty, but my older selection "Rich" returns back to the old font size.

Edit 3: This problem is mentioned in Sams Teach Yourself WPF in 24 Hours first printing July 2008, page 135, "Making the Text Editor Work as Expected", item 9. I didn't understand the explanation there and created a short sample illustrating that specific problem.

like image 340
hattenn Avatar asked Oct 22 '22 13:10

hattenn


1 Answers

What seems to be happening is that when you click to clear the selection, this causes your TextBox.SelectionChanged event handler (MyTextBox_SelectionChanged) to be invoked while the Selection represents an empty selection (i.e., just an insertion point). Your handler sets the combo box's SelectedValue, using the empty selection's font size, which is a perfectly reasonable thing to do even if the selection is empty. (The insertion point still has a font size.)

Changing the SelectedValue of course causes your ComboBox.Selection event handler (FontSize_SelectionChanged) to run. And because that event handler has no easy way to distinguish between an event caused by the user selecting a new value, and the event being caused by your code changing the SelectedValue property, it goes ahead and attempts to modify the selection's font size, which you probably don't want to be doing in this particular case.

Even so, you'd think it'd be OK, because the selection is empty, and you're just attempting to set its font size to be whatever its font size already is. But here's the weird thing: when you call ApplyPropertyValue on this empty selection, it appears to set the FontSize for the entire document.

So the effect is that when you click to clear the selection, your code sets the entire document's font size to be whatever the font size is at the point where you click.

I suspect that's a bug in ApplyPropertyValue, because it only happens if the text you select initially was selected by dragging from left to right, starting at the very first character. Then again, it's not entirely clear what the behaviour is meant to be if you apply formatting to an empty selection. So perhaps this is more just a case of invoking undefined behaviour rather than hitting a definite bug in WPF.

In any case, a reasonable way to fix this is by modifying your combo box change handler:

if (FontSize.SelectedItem != null && !MyTextBox.Selection.IsEmpty)
{
    MyTextBox.Selection.ApplyPropertyValue(
        TextBlock.FontSizeProperty, FontSize.SelectedItem);
}

This only attempts to change the selection's font size if the selection is non-empty.

like image 178
Ian Griffiths Avatar answered Oct 24 '22 10:10

Ian Griffiths