2014-09-19 2 views
0

У меня есть собственный элемент управления WPF, который наследуется от RichTextBox. Я хочу иметь возможность изменять FlowDocument из richtextbox всякий раз, когда текст изменяется. Для демонстрации, скажем, что у нас есть это:WPF получить блок документов RichTextBox с измененным текстом

<MyCustomRichTextbox> 
    <FlowDocument> 
     <Paragraph>This is the first paragraph</Paragraph> 
     <Paragraph>And this is the second</Paragraph> 
    </FlowDocument> 
</MyCustomRichTextbox> 

и всякий раз, когда текст изменяется (например, кто-то типы в контроле), весь содержащий пункт окрашен в красный цвет.

Мне кажется, что две вещи, которые должны произойти, чтобы достичь этого:

  1. мне нужно каким-то образом получить блок, содержащий измененный текст из MyCustomRichTextbox.Document.Blocks
  2. Мне нужно изменить этот блок

К сожалению, метод OnTextChanged не дает способа получить измененный блок. Я смог использовать LINQ и TextChangedEventArgs.Offset для получения блока, но я обеспокоен тем, что этот подход приведет к недопустимым замедлениям с большими документами (поскольку он должен перечислять каждый блок каждый раз, когда набирается символ). Есть ли лучший способ получить содержащий абзац?

Я знаю, что могу кэшировать ссылку на «Последний измененный блок» и проверить, не изменился ли он, но это не помогло бы в сценарии случайного доступа.

ответ

1

Если я правильно понимаю вашу проблему, вы хотите выделить содержащуюся параграф текущего выбора (текущая позиция каретки). Таким образом, очевидно, что каждый раз, когда изменяется Selection, вы должны получить содержащийся параграф. Затем вы можете просто изменить Foreground на Brushes.Red. К счастью, содержащая параграф, по-видимому, ссылается на TextPointer, и процесс ее обнаружения почти мгновен. (TextPointer имеет свойство, называемое Paragraph). Вот подробный код:

Paragraph p = null; 
//Suppose rtb is the name of your RichtTextBox 
private void UpdateContainingBlockState() { 
    if (p != rtb.Selection.Start.Paragraph){ 
    if (p != null) p.Foreground = Brushes.Black; 
    p = rtb.Selection.Start.Paragraph; 
    if (p != null) p.Foreground = Brushes.Red; 
    } 
} 

//The SelectionChanged event handler for your RichTextBox 
private void selectionChangedHandler(object sender, RoutedEventArgs e){ 
    UpdateContainingBlockState(); 
} 

Частота изменения Выбор довольно велик (каждый раз, когда вы нажимаете почти клавиши, которые могут вызвать смену выбора). Поэтому, если ваш документ большой и вы плохо себя чувствуете при наборе текста, пришло время перейти на следующий более сложный код. Вы также можете попытаться использовать подход Threading (или использовать Task), чтобы поместить вызов UpdateContainingBlockState() в другой поток, но будьте осторожны при доступе через кросс-поток. Здесь я использую другой подход, идея называется UpdateContainingBlockState()в нужное время, то есть, когда фактическое изменение выбора может перепрыгнуть между абзацами. При вводе обычных печатных символов выбор всегда будет в текущем абзаце (поэтому нам не нужно звонить UpdateContainingBlockState()), если только вы не наберете . Введите. Обычно мы будем вызывать метод, когда пользователь вводит управляющий ключ (клавиши со стрелками, home, end, Enter, ...). Мы также должны вызвать метод, если RichTextBox будет сфокусирован, и если пользователь нажимает мышью на RichTextBox. Вы можете видеть, что почти типизированные символы не будут вызывать вызов метода, поэтому он улучшит производительность намного больше, чем код выше (конечно, это может быть реализовано только в том случае, если документ большой). Вот подробный код:

//should add this using at the beginning 
using System.Runtime.InteropServices; 

[DllImport("user32")] 
private static extern int MapVirtualKey(int ucode, int mapType); 

//The KeyUp event handler for your RichTextBox 
private void keyUp_Handler(object sender, KeyEventArgs e){ 
    if (char.IsControl((char) MapVirtualKey(KeyInterop.VirtualKeyFromKey(e.Key),0x2))) 
    UpdateContainingBlockState(); 
} 
//The PreviewMouseUp event handler for your RichTextBox 
private void previewMouseUp_Handler(object sender, MouseButtonEventArgs e){ 
    //UpdateContainingBlockState(); 
    //Calling UpdateContainingBlockState() directly will cause a small issue 
    //So we use this instead 
    Task.Run(() => Dispatcher.Invoke(() => UpdateContainingBlockState())); 
} 
//The GotKeyboardFocus event handler for your RichTextBox 
private void gotKeyboardFocus_Handler(object sender, 
             KeyboardFocusChangedEventArgs e){ 
    UpdateContainingBlockState(); 
} 
Смежные вопросы