2014-09-19 49 views
0

我有一個從RichTextBox中繼承的自定義WPF控件。我希望能夠在文本發生更改時修改richtextbox的FlowDocument。爲了演示,讓我們說,我們有這個:文本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

如果我理解你的問題正確,要突出顯示當前選擇的含第(插入符的當前位置)。所以很顯然,每次選擇更改時都必須獲取包含的段落。然後,您可以將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(); 
} 

更改選擇的頻率相當高(每次按幾乎可以導致選擇更改的鍵)。因此,如果您的文檔很大,並且您在輸入時意識到性能不佳,那麼現在是切換到下一個更復雜的代碼的時候了。您還可以嘗試使用線程方法(或使用任務)將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(); 
}