Как нарисовать границу вокруг слова в RichTextBox? - PullRequest
10 голосов
/ 28 мая 2011

Допустим, у меня есть 2 TextPointers.Один указывает на начало слова, а другой на конец слова.

Я хотел бы нарисовать однопиксельную границу вокруг слова.Как бы я пошел по этому поводу?Граница должна быть привязана к слову и перемещаться вместе с ним, когда пользователь печатает или прокручивает ..

Я уже пробовал TextDecorations с DrawingBrush, но не смог придумать что-либо пригодное для использования.

1 Ответ

7 голосов
/ 28 мая 2011

Я сделал нечто подобное, только подчеркнув текст в TextBox. Принципал, кажется, в основном тот же.

  1. Добавьте AdornerDecorator, содержащий ваш RichTextBox, но внутри ScrollViewer.

    <Border ...>
        <ScrollViewer ... >
            <AdornerDecorator>
                <RichTextBox
                    x:Name="superMagic"
                    HorizontalScrollBarVisibility="Hidden"
                    VerticalScrollBarVisibility="Hidden"
                    BorderBrush="{x:Null}"
                    BorderThickness="0"
                    ...
                    />
            </AdornerDecorator>
        </ScrollViewer>
    </Border>
    
  2. Создать Adorner для рендеринга прямоугольника и добавить его в AdornerLayer

    void HostControl_Loaded(object sender, RoutedEventArgs e)
    {
        _adorner = new RectangleAdorner(superMagic);
    
        AdornerLayer layer = AdornerLayer.GetAdornerLayer(superMagic);
        layer.Add(_adorner);
    }
    
  3. Обладатель должен перехватить событие TextChanged RichTextBox. Все, что вам нужно сделать, это вызвать InvalidateVisuals() через диспетчера, используя DispatcherPriority.Background, чтобы убедиться, что он отображается после текстового поля. Я не знаю, является ли это проблемой для RichTextBox, но получение координат символа из TextBox возможно только в том случае, если оно было визуализировано хотя бы один раз с момента последнего изменения содержимого.

    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. Переопределите Adorner.OnRender(), чтобы нарисовать прямоугольник, используя TextPointer.GetCharacterRect(), чтобы получить координаты.

    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));
    }
    

Примечание: Хотя оригинальный код работал хорошо, я написал его давным-давно и не проверял свои приспособления для этого ответа. По крайней мере, это должно помочь вам встать на правильный путь.

Кроме того, это не относится к случаям, когда слово разбивается по строкам, но не должно быть слишком сложным для обслуживания.

...