Стиль многострочного текстового поля Wpf, который влияет на отдельные строки - PullRequest
0 голосов
/ 11 октября 2019

Мне нужно создать многострочное текстовое поле, которое меняет фон для персонажей после того, как строка станет длиннее 80 символов. Так что, если кто-то наберет предложение из 85 символов, последние 5 символов будут иметь желтый фон. Мне бы хотелось, чтобы эта функция была частью стиля, потому что в настоящее время мы пытаемся сделать это с помощью логики в коде, и она отстает, когда кто-то печатает быстро.

текущая подсветка imlp

 private void HighlightLines()
        {
            try
            {
                //// x is the distance in the row from the left side of the Textbox. e.g. Each character is one unit.
                int x = 0;
                //// y is the distance in the columns from the top of the Textbox. e.g. Each character is one unit.
                int y = 0;
                //// lines is the number of newline characters found in the Text.
                int lines = 0;
                //// Point 1 is the starting point of the range that needs to be highlighted.
                TextPointer point1 = this.Document.ContentStart;
                //// Point 2 is the end point of the range that needs to be highlighted.
                TextPointer point2 = this.Document.ContentStart;
                //// Range is the distance from Point 1 to Point 2 needed to apply the Yellow color to the area past 69 characters.
                TextRange range;
                //// Additional Ranges is the collection of all the ranges that need to be Yellow.
                this.AdditionalRanges = new ObservableCollection<TextRange>();

                //// Count the number of lines.
                for (int i = 0; i < this.Text.Length; i++)
                {
                    if (this.Text[i] == '\n')
                    {
                        lines++;
                        this.AdditionalRanges.Add(new TextRange(this.Document.ContentStart, this.Document.ContentEnd));
                    }
                }
                //// This map is used to differentiate which lines need to be colored. (True means the range is over 69 characters. False means the opposite).
                bool[] map = new bool[lines];

                //// Traverse the whole text.
                for (int i = 0; i < this.Text.Length; i++)
                {
                    var currentCharacter = this.Text[i];
                    var newLineCharacter = '\n';

                    if (currentCharacter == newLineCharacter)
                    {
                        var pointDifference = point1.GetOffsetToPosition(point2);
                        point1 = point1.GetPositionAtOffset(pointDifference);
                        x = 0;
                        y++;
                    }
                    else if (x > 69)
                    {
                        range = new TextRange(point1, point2);
                        this.AdditionalRanges[y] = range;
                        map[y] = true;
                        if (point2.GetNextInsertionPosition(LogicalDirection.Forward) != null)
                        {
                            point2 = point2.GetNextInsertionPosition(LogicalDirection.Forward);
                        }
                    }
                    else if (point1.GetNextInsertionPosition(LogicalDirection.Forward) != null && point2.GetNextInsertionPosition(LogicalDirection.Forward) != null)
                    {
                        point1 = point1.GetNextInsertionPosition(LogicalDirection.Forward);
                        point2 = point2.GetNextInsertionPosition(LogicalDirection.Forward);
                        x++;
                    }
                }

                //// Make everything white.
                foreach (var item in this.AdditionalRanges)
                {
                    item.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.White);
                }

                //// Make the appropriate ranges Yellow.
                for (int i = 0; i < this.AdditionalRanges.Count; i++)
                {
                    if (map[i])
                    {
                        this.AdditionalRanges[i].ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.Yellow);
                    }
                }
            }
            catch (Exception e)
            {
                // drop exception. this has only broke once and we dont exactly know why. 
            }
        }

1 Ответ

0 голосов
/ 11 октября 2019

Вероятно, он отстает, потому что ваш метод HighlightLines() сканирует весь документ каждый раз, и я предполагаю, что он вызывается после каждого нажатия клавиши. В идеале вы хотите повторно сканировать только те части текста, которые изменились. К счастью, событие TextChanged предоставляет точное смещение изменений.

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

RichTextBox txt;
...
bool suppressChanges = false;
private void Txt_TextChanged(object sender, TextChangedEventArgs e)
{
    if (!suppressChanges)
    {
        // suppress changes because changing highlights will trigger the event again
        suppressChanges = true;
        foreach (var change in e.Changes)
        {
            var changeStart = txt.Document.ContentStart.GetPositionAtOffset(change.Offset);
            TextRange changedRange;
            if (change.AddedLength > 0)
                changedRange = new TextRange(changeStart, changeStart.GetPositionAtOffset(change.AddedLength));
            else
                changedRange = new TextRange(changeStart, changeStart);
            SetRangeColors(changedRange);
        }

        //unsuppress changes
        suppressChanges = false;
    }
}

void SetRangeColors(TextRange range)
{
    // Scan one line at a time starting with the beginning of the range
    TextPointer current = range.Start.GetLineStartPosition(0);
    while (current != null && current.CompareTo(range.End) < 0)
    {
        // find the next line or the end of the document
        var nextLine = current.GetLineStartPosition(1, out int lines);
        TextPointer lineEnd;
        if (lines > 0)
            lineEnd = nextLine.GetNextInsertionPosition(LogicalDirection.Backward);
        else
            lineEnd = txt.Document.ContentEnd;
        var lineRange = new TextRange(current, lineEnd);

        // clear properties first or the offsets won't match the characters
        lineRange.ClearAllProperties();
        var lineText = lineRange.Text;
        if (lineText.Length > 69)
        {
            var highlight = new TextRange(current.GetPositionAtOffset(70), lineEnd);
            highlight.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.Yellow);
        }

        // advance to the next line
        current = lineEnd.GetLineStartPosition(1);
    }
}
...