由於公司涉及到聊天對話框的功能,就想到了RichTextBox,查閱相關資料,總結下:
一、RichTextBox的內容相關的類
1.1RichTextBox的內容結構
RichTexBox是個可編輯控件,可編輯我們很容易想到word的可編輯,在word里面我們經常會新建一個Document,然后一段一段的編寫,有的時間會插入圖片,或者是特殊的文本。同樣RichTextBox也是一個以Document為主體的一個控件,Document也有段落(Paragraph),不過段落中是分為小片小片(Inline),我們可以理解為這些Inline被一條線串在段落(Paragragh)中。除此之外,還有些段落(BlockUIContainer)是只含有一個UI元素的,也即獨立成段。下面給出其大致的內容結構。
1.2RichTextBox內容相關的類
FlowDocument中有個BlockCollection類型的Blocks屬性,也就是“文檔”包括的段落。主要有兩種類型的Paragraph和BlockUIContainer兩種類型的段落,其都繼承自Block抽象類。下面介紹一下Paragraph的小弟們,從上圖可以看出來小弟很多,其實都是和InLine有關,通常使用Run類表示文本,我們可以通過其屬性類控制他的背景色,字體顏色,字體大小等。Hyperlink可以用來指定超鏈接。。。其中InlineContainer是有些不一樣,其有個兒子是UIElement。看后綴名就知道BlockUIContainer和InlineContainer一定有些相似之處,相似的地方是BlockUIContainer也是有個兒子UIElement。當然一個是Inline類型,一個是Block類型。
光說不練,不是好漢,接下來就舉幾個例子來說明上面的類的特點:
1.3RichTextBox內容相關類的演示
下面我們就用wpf實現與RichtextBox內容相關的類的操作。先說一下需求,在“文檔”上面寫兩端文字,一個是Paragraph類型的段落,一個是BlockContainer類型的段落。其中Paragraph中包括一個紅色的文本Run、一個超鏈接Hyperlink,一個InlineContainer的內含有個TextBlock;BlockUIContainer含有一個圖片以及圖片說明。
//定義一個段落Paragraph Paragraph paragraph = new Paragraph(); //run Run run = new Run() { Text = "我是紅色的Run", Background = new SolidColorBrush(Color.FromRgb(255, 0, 0)) }; paragraph.Inlines.Add(run); //Hyperlink Hyperlink hyperlink = new Hyperlink(); hyperlink.Inlines.Add("我是博客園主頁的鏈接"); hyperlink.MouseLeftButtonDown += ((s, arg) => { Process proc = new Process(); proc.StartInfo.FileName = "http://www.cnblogs.com"; proc.Start(); }); paragraph.Inlines.Add(hyperlink); //InlineUIContainer InlineUIContainer inlineUIContainer = new InlineUIContainer() { Child = new TextBlock() { Text = "我是的TextBlock,哈哈" } }; paragraph.Inlines.Add(inlineUIContainer); rtb.Document.Blocks.Add(paragraph); //下面是BlockUIContainer部分 Grid grid = new Grid(); RowDefinition row1 = new RowDefinition(); grid.RowDefinitions.Add(row1); RowDefinition row2 = new RowDefinition(); grid.RowDefinitions.Add(row2); row2.Height = new GridLength(100); //定義圖片,注意設置資源的類型,始終復制和嵌入 Image image = new Image() { Source = new BitmapImage(new Uri(("Images/vs.png"), UriKind.Relative)) }; image.Height = 30; image.SetValue(Grid.RowProperty, 0); grid.Children.Add(image); image.Visibility = Visibility.Visible; row2.SetBinding(RowDefinition.HeightProperty, new Binding("Height") { Source = image }); //定義說明 TextBlock block = new TextBlock(); block.Text = "我是圖片說明"; block.SetValue(Grid.RowProperty, 1); grid.Children.Add(block); BlockUIContainer blockUIContainer = new BlockUIContainer(grid) ; rtb.Document.Blocks.Add(blockUIContainer);
show一下結果
二、RichTextBox的定位操作
首先給出定位操作的意思,就是在光標閃爍的地方操作。在看下面的文章時,我希望在我們心中有個word軟件。下面主要介紹關於光標的位置和在光標的位置插入相應的元素。
2.1光標的位置
無論光標是否選中一段文字,都有可以獲取rtb.Selection的開始位置(Start)和結束位置(End)。可以通過開始位置和結束位置來獲取光標位置所在的段落(Paragraph)和父對象(Parent)。父對象其實也就是如果光標在run中間,獲取到的就是Run,當選中的是TextBlock時,父對象就是Paragraph。這兩個屬性各有優略,段落可能獲得空值,在操作的時間拋出空指針異常,但得到的類型確定(是屬於Block類),父對象不會拋出空指針異常,但是其類型不確定。
2.2在光標處插入對象和替換對象
下面來看看相關內容類的構造函數:
Run:
public Run(string text, TextPointer insertionPosition) Span: public Span(Inline childInline, TextPointer insertionPosition); public Span(TextPointer start, TextPointer end); Hypelink: public Hyperlink(Inline childInline, TextPointer insertionPosition); public Hyperlink(TextPointer start, TextPointer end); InlineContainer public InlineUIContainer(UIElement childUIElement, TextPointer insertionPosition);
……以上我列出了幾個非默認的構造類,其他的相關類,就不一一列出。從參數可以看出我們很容易的可以在光標處插入對象。下面只給出其代碼片段:
TextPointer textPointer = rtb.Selection.Start;
Run run = new Run("測試", textPointer);
接着是替換對象,我們想把選中的內容替換成指定的內容。下面我給出其實例:
//獲取選中部分的開始位置 TextPointer textPointer = rtb.Selection.Start; //在開始位置插入內容 Run run = new Run("測試", textPointer); //在插入內容的結尾到原來選中部分的結尾——原來選中部分的文字 清除掉 TextPointer pointer = run.ContentEnd; TextRange textRange = new TextRange(pointer, rtb.Selection.End); textRange.Text = ""; //如果使用下面的會把剛剛插入的內容一並刪除 //rtb.Selection.Text = "";
對於有開始位置和結束位置的構造函數,可以使用相同的位置來構造,如下:
Hyperlink hypelink = new Hyperlink(textPointer, textPointer);
三、WPF中RichTextBox和工具欄的協同工作
WPF中RichTextBox可以與工具欄協同的命令:ApplicationCommands 和EditingCommands,在默認只有一個RichTextBox時,工具欄中的按鈕的命令設置為系統命令時就會自動的把命令的目標設置為RichTextBox。下面給出一個實例:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/> <RowDefinition Height="5*"/> </Grid.RowDefinitions> <ToolBarTray> <ToolBar> <Button Command="Undo">撤銷</Button> <Button Command="EditingCommands.ToggleBold" ToolTip="Bold">B</Button> </ToolBar> </ToolBarTray> <RichTextBox Name="rtb" AcceptsTab="True" Grid.Row="1" Grid.ColumnSpan="4"></RichTextBox> <!--<Button x:Name="btnAddElement" Content="添加元素" Grid.Row="1" Click="btnAddElement_Click_1" Grid.Column="0" Margin="5"></Button>--> </Grid>
當點擊撤銷按鈕時,RichTextBox會撤銷操作,同樣Bold會黑體操作。當然也可使用下面代碼來顯式指定按鈕的命令目標。
<Style TargetType="{x:Type Button}" x:Key="ImageButtonStyle"> <Setter Property="CommandTarget" Value="{Binding ElementName=rtb}"></Setter> </Style>
四、RichTextBox的XAML轉化成HTML以及HTML轉化成XAML
在使用RichTextBox時,可能會遇到存儲和顯示不一致的問題,如在數據庫里面是使用fck編輯的html格式,顯示的時間要使用RichTextBox顯示。或者是使用RichTextBox編輯的內容,要使用html顯示。那么這樣就會遇到轉化問題,微軟在這方面已經有為我們做好的類。下面給出鏈接:
https://github.com/stimulant/SocialStream/tree/master/XAMLConverter
以上轉化針對簡單的轉化,或者是提供了思路,如果遇到特殊的自定義容器,還需要自己去添加。除上面的鏈接之外,很有必要給出下面兩個方法。
//把richtextBox內容轉成字符串形式 string strDoc=System.Windows.Markup.XamlWriter.Save(rtb.Document); //上面的逆操作 StringReader stringReader = new StringReader(strDoc); XmlReader xmlReader = XmlReader.Create(stringReader); FlowDocument flowDocument = XamlReader.Load(xmlReader) as FlowDocument;
五、零碎知識點總結
1. 取得已被選中的內容:
(1)使用 RichTextBox.Document.Selection屬性
(2)訪問RichTextBox.Document.Blocks屬性的“blocks”中的Text
2. 在XAML中增加內容給RichTextBox:
<RichTextBox IsSpellCheckEnabled="True">
<FlowDocument>
<Paragraph> <!-- 這里加上你的內容 -->
This is a richTextBox. I can <Bold>Bold</Bold>,
<Italic>Italicize</Italic>,
<Hyperlink>Hyperlink stuff</Hyperlink>
right in my document.
</Paragraph>
</FlowDocument>
</RichTextBox>
3. 縮短段間距,類似<BR>,而不是<P>
方法是使用Style定義段間距:
<RichTextBox>
<RichTextBox.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0"/>
</Style>
</RichTextBox.Resources>
<FlowDocument>
<Paragraph>
This is my first paragraph... see how there is...
</Paragraph>
<Paragraph>
a no space anymore between it and the second paragraph?
</Paragraph>
</FlowDocument>
</RichTextBox>
private void LoadText(RichTextBox richTextBox, string txtContent)