textrange теряет ссылку на textpointer?

Я использовал некоторый код, который нашел здесь, чтобы попытаться изменить регистр текста в потоковом документе. Он корректно изменяет текст, однако все форматирование теряется (полужирный шрифт, курсив и т. Д.), И когда я сохраняю документ в файл XML, весь текст заканчивается в первом прогоне документа, а все остальные прогоны становятся пустыми.

    private void ChangeCase()
    {
        TextPointer start = mergedDocument.ContentStart;
        TextPointer end = mergedDocument.ContentEnd;
        List<TextRange> textToChange = SplitToTextRanges(start, end);
        ChangeCaseToAllRanges(textToChange);
    }

    private List<TextRange> SplitToTextRanges(TextPointer start, TextPointer end)
    {
        List<TextRange> textToChange = new List<TextRange>();
        var previousPointer = start;
        for (var pointer = start; (pointer != null && pointer.CompareTo(end) <= 0); pointer = pointer.GetPositionAtOffset(1, LogicalDirection.Forward))
        {
            var contextAfter = pointer.GetPointerContext(LogicalDirection.Forward);
            var contextBefore = pointer.GetPointerContext(LogicalDirection.Backward);
            if (contextBefore != TextPointerContext.Text && contextAfter == TextPointerContext.Text)
            {
                previousPointer = pointer;
            }
            if (contextBefore == TextPointerContext.Text && contextAfter != TextPointerContext.Text && previousPointer != pointer)
            {
                textToChange.Add(new TextRange(previousPointer, pointer));
                previousPointer = null;
            }
        }
        textToChange.Add(new TextRange(previousPointer ?? end, end));
        return textToChange;
    }

    private void ChangeCaseToAllRanges(List<TextRange> textToChange)
    {
        Func<string, string> caseChanger;
        ComboBoxItem cbi = cb_Case.SelectedItem as ComboBoxItem;
        var textInfo = CultureInfo.CurrentUICulture.TextInfo;

        if (cbi == null || (string)cbi.Tag == "none")
        {
            return;
        }
        else if((string)cbi.Tag == "title")
        {
            caseChanger = (text) => textInfo.ToTitleCase(text);
        }
        else if ((string)cbi.Tag == "upper")
        {
            caseChanger = (text) => textInfo.ToUpper(text);
        }
        else if ((string)cbi.Tag == "lower")
        {
            caseChanger = (text) => textInfo.ToLower(text);
        }
        else
            return;

        foreach (var range in textToChange)
        {
            if (!range.IsEmpty && !string.IsNullOrWhiteSpace(range.Text))
            {
                System.Diagnostics.Debug.WriteLine("Casing: " + range.Text);
                System.Diagnostics.Debug.WriteLine("\tat: " + 
                    range.Start.GetOffsetToPosition(mergedDocument.ContentStart) +
                    " ," +
                    range.End.GetOffsetToPosition(mergedDocument.ContentStart));
                range.Text = caseChanger(range.Text);
            }
        }
    }

Я не вижу причин, почему этот код не работает должным образом. Похоже, что указатели текста в объекте textrange перенаправляются в начало документа.

1 ответ

Решение

При установке TextRange.Text он сначала удаляет выделение, сообщая TextContainer (FlowDocument) об удалении этого содержимого. Если этот контент оказался целым Inline со свойствами зависимости стиля, тогда до свидания! Таким образом, он не только получает неустановленный текст, но и устанавливает его

Поскольку вы хотите сохранить свои существующие встроенные объекты, вы можете перебирать весь FlowDocument, чтобы найти их и установить их текстовое свойство.

Вот вспомогательный метод, который поддерживает только абзацы и находит все строки во всем выделении (эта логика намного проще, если вы всегда выполняете Document.ContentStart и Document.ContentEnd). Вы можете расширить это, чтобы включить встроенные строки в списки, ListItems и гиперссылки, если вам нужно (следуя аналогичному шаблону).

После этого вы сможете установить свойство Text для каждой из этих строк.

    List<Inline> GetInlines(TextRange selection)
    {
        var inlines = new List<Inline>();

        foreach (var block in Document.Blocks.Where(x => selection.Start.CompareTo(x.ContentEnd) < 0 && selection.End.CompareTo(x.ContentStart) > 0))
        {
            var paragraph = block as Paragraph;
            if (paragraph != null)
            {
                inlines.AddRange(paragraph.Inlines.Where(x => selection.Start.CompareTo(x.ContentEnd) < 0 && selection.End.CompareTo(x.ContentStart) > 0));
            }
        }

return inlines;

Изменить: вы хотите привести их к Run или Span для доступа к свойству text. Вы даже можете просто удалить Inline и получить эти типы (вероятно, просто Run).

Другие вопросы по тегам