views:

187

answers:

2

I've created a highlighting mechanism for a RichTextBox in Silverlight 4. It'll get character positions and draw rectangle(s) over the text.

The trouble I have now is with scrolling on the RichTextBox. As I scroll all of my precious highlighting gets left behind. Is there any way I can add an event handler to a scroll event and/or a scrolling position of the RichTextBox? Or is there some better way in which I can link the position of the highlighting rectangles to the RichTextBox?

+1  A: 

The trick would be to get what ever panel (I guess its a Canvas?) that you are overlaying the RichTextBox with to actually exist within the same ScrollViewer that rich text exists in.

The following is very rough idea but should get you on the path to reasonable solution.

You can do this using a custom style for the RichTextBox. The default style for this control can be found here.

Copy this style into a resource in your containing UserControl and point your RichTextBox Style property at it. So far nothing is different but now you can play about with the template. The relevant portion currently looks like this:-

<Border x:Name="MouseOverBorder" BorderThickness="1" BorderBrush="Transparent">
    <ScrollViewer x:Name="ContentElement" Padding="{TemplateBinding Padding}" BorderThickness="0" IsTabStop="False" />
</Border>

Now we can tweak it like this:-

<Border x:Name="MouseOverBorder" BorderThickness="1" BorderBrush="Transparent">
    <ScrollViewer Padding="{TemplateBinding Padding}" BorderThickness="0" IsTabStop="False">
        <Grid>
            <ContentControl x:Name="ContentElement" />
            <Canvas x:Name="HighlightOverlay" />
        </Grid>
    </ScrollViewer>
</Border>

You'll note that we've moved the name "ContentElement" from the ScrollViewer to the new ContentControl. Having a FrameworkElement called "ContentElement" is the only feature that the RichTextBox stipulates about its template.

Now overlaying this ContentControl we can place a Canvas where you can place your highlighting rectangles. If the user scrolls this RichTextBox the whole Grid containing both the Content and the Highlights will scroll together.

The only remaining trick is acquiring the "HighlightOverlay" so that you can add your rectangle to it. Here is some code that will grab it:-

    private Canvas HightlightOverlay;
    public MyUserControl()
    {
        InitializeComponent();
        MyRichText.LayoutUpdated += MyRichText_LayoutUpdated;
    }

    void MyRichText_LayoutUpdated(object sender, EventArgs e)
    {
        HightlightOverlay = MyRichText.Descendents()
            .OfType<Canvas>()
            .FirstOrDefault(elem => elem.Name == "HighlightOverlay");
    }

You will be wondering where the Descendents method is coming from, it is here.

AnthonyWJones
Brilliant! I was about to go down a completely different path of actually formatting the text to accomplish my highlighting but that has its own issues. This works so well and finally defeats the problem I've been trying to crack for a long time. I just had to make a couple tweaks to make your xaml work, which I'll add below.
Steve Wortham
A: 

Anthony W Jones came up with a brilliant solution. There were just a couple tweaks to the XAML I had to make.

As suggested I started with this inside the template:

<Border x:Name="MouseOverBorder" BorderThickness="1" BorderBrush="Transparent">
    <ScrollViewer Padding="{TemplateBinding Padding}" BorderThickness="0" IsTabStop="False">
        <Grid>
            <ContentControl x:Name="ContentElement" />
            <Canvas x:Name="HighlightOverlay" />
        </Grid>
    </ScrollViewer>
</Border>

But the ContentControl messed things up somehow and you can't actually type into the RichTextBox anymore. Also, the scroll bars weren't showing up.

But I found the two changes necessary to make this work:

<Border x:Name="MouseOverBorder" BorderBrush="Transparent" BorderThickness="1">
    <ScrollViewer BorderThickness="0" IsTabStop="False" Padding="{TemplateBinding Padding}" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
        <Grid>
            <Grid x:Name="ContentElement" />
            <Canvas x:Name="HighlightOverlay" />
        </Grid>
    </ScrollViewer>
</Border>

Adding HorizontalScrollBarVisibility="Auto" and VerticalScrollBarVisibility="Auto" brought the scroll bars back, and simply using Grid instead of the ContentControl made the RichTextBox editable again.

Steve Wortham
Its strange, I've justed test my approach and the box was editable but the vertical scrollbar was appearing even though it wasn't required. I did think it would need to a little more playing with to get it working in desirable way. I'm glad you got it working.
AnthonyWJones