Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
298 views
in Technique[技术] by (71.8m points)

c# - How to draw border around a word in RichTextBox?

Let's say I have 2 TextPointers. One pointing at the beginning of a word and the other at the end of the word.

I would like to draw single pixel border around the word. How would I go about this? The border should be tied to the word and move with it when user types or scrolls..

I already tried TextDecorations with DrawingBrush but couldn't come up with anything usable.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I have done something similar, only underlining text in a TextBox. The principal seems to be mostly the same.

  1. Add an AdornerDecorator containing your RichTextBox but inside a ScrollViewer.

    <Border ...>
        <ScrollViewer ... >
            <AdornerDecorator>
                <RichTextBox
                    x:Name="superMagic"
                    HorizontalScrollBarVisibility="Hidden"
                    VerticalScrollBarVisibility="Hidden"
                    BorderBrush="{x:Null}"
                    BorderThickness="0"
                    ...
                    />
            </AdornerDecorator>
        </ScrollViewer>
    </Border>
    
  2. Create an Adorner to render the rectangle and add it to the AdornerLayer

    void HostControl_Loaded(object sender, RoutedEventArgs e)
    {
        _adorner = new RectangleAdorner(superMagic);
    
        AdornerLayer layer = AdornerLayer.GetAdornerLayer(superMagic);
        layer.Add(_adorner);
    }
    
  3. The adorner should hook the TextChanged event of the RichTextBox. All you need to do is call InvalidateVisuals() via the dispatcher using DispatcherPriority.Background to ensure it is rendered after the text box. I don't know if it's an issue for the RichTextBox, but getting the character coordinates from a TextBox is only possible if it has been rendered at least once since it's content last changed.

    class RectangleAdorner : Adorner
    {
        public RectangleAdorner(RichTextBox textbox)
            : base(textbox)
        {
            textbox.TextChanged += delegate
            {
                SignalInvalidate();
            };
        }
    
        void SignalInvalidate()
        {
            RichTextBox box = (RichTextBox)this.AdornedElement;
            box.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)InvalidateVisual);
        }
    
        // ...
    }
    
  4. Override Adorner.OnRender() to draw the box using TextPointer.GetCharacterRect() to get the coordinates.

    protected override void OnRender(DrawingContext drawingContext)
    {
        TextPointer start;
        TextPointer end;
    
        // Find the start and end of your word
        // Actually, if you did this in the TextChanged event handler,
        // you could probably save some calculation time on large texts
        // by considering what actually changed relative to an earlier
        // calculation. (TextChangedEventArgs includes a list of changes
        //  - 'n' characters inserted here, 'm' characters deleted there).
    
        Rect startRect = start.GetCharacterRect(LogicalDirection.Backward);
        Rect endRect = end.GetCharacterRect(LogicalDirection.Forward);
    
        drawingContext.DrawRectangle(null, pen, Rect.Union(startRect, endRect));
    }
    

Note: Although the original code worked well, I wrote it a long time ago and have not tested my adaptions for this answer. It should at least help put you on the right path.

Also, this does not handle cases where the word is split across lines, but shouldn't be too hard to cater for.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...