由于公司涉及到聊天对话框的功能,就想到了RichTextBox,查阅相关资料,总结下:

一、RichTextBox的内容相关的类

1.1RichTextBox的内容结构

RichTexBox是个可编辑控件,可编辑我们很容易想到word的可编辑,在word里面我们经常会新建一个Document,然后一段一段的编写,有的时间会插入图片,或者是特殊的文本。同样RichTextBox也是一个以Document为主体的一个控件,Document也有段落(Paragraph),不过段落中是分为小片小片(Inline),我们可以理解为这些Inline被一条线串在段落(Paragragh)中。除此之外,还有些段落(BlockUIContainer)是只含有一个UI元素的,也即独立成段。下面给出其大致的内容结构。

WPF RichTextBox相关总结_perl

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.cn.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);

WPF RichTextBox相关总结_perl_02

show一下结果

WPF RichTextBox相关总结_perl_03


二、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>

4. 从文件中读出纯文本文件后放进RichTextBox或直接将文本放进RichTextBox中:

private void LoadTextFile(RichTextBox richTextBox, string filename)
{     richTextBox.Document.Blocks.Clear();  
     using (StreamReader streamReader = File.OpenText(filename))
  {         
     Paragraph paragraph = new Paragraph();
      paragraph.Text = streamReader.ReadToEnd();           
    richTextBox.Document.Blocks.Add(paragraph);    
  }
}
private void LoadText(RichTextBox richTextBox, string txtContent){    
  richTextBox.Document.Blocks.Clear();
   Paragraph paragraph = new Paragraph();     
  paragraph.Text = txtContent;    
  richTextBox.Document.Blocks.Add(paragraph);
}

 

5. 取得指定RichTextBox的内容:

private string GetText(RichTextBox richTextBox)
{        
  TextRange textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);        
  return textRange.Text;
}

6. 将RTF (rich text format)放到RichTextBox中:

   

private static void LoadRTF(string rtf, RichTextBox richTextBox)
        {            
    if (string.IsNullOrEmpty(rtf))
     {                
      throw new ArgumentNullException();    
           }           
      TextRange textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);     
       using (MemoryStream rtfMemoryStream = new MemoryStream())
    {                
      using (StreamWriter rtfStreamWriter = new StreamWriter(rtfMemoryStream))
    {                     
      rtfStreamWriter.Write(rtf);   
                  rtfStreamWriter.Flush();    
                 rtfMemoryStream.Seek(0, SeekOrigin.Begin);
                    //Load the MemoryStream into TextRange ranging from start to end of RichTextBox.  
                   textRange.Load(rtfMemoryStream, DataFormats.Rtf);       
          }            
  }        
}

7. 将文件中的内容加载为RichTextBox的内容

private static void LoadFile(string filename, RichTextBox richTextBox)  
       {           
  if (string.IsNullOrEmpty(filename))
{              
   throw new ArgumentNullException();            
}           
  if (!File.Exists(filename))
{                
throw new FileNotFoundException();   
          }   
          using (FileStream stream = File.OpenRead(filename))
{              
   TextRange documentTextRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);   
              string dataFormat = DataFormats.Text;         
        string ext = System.IO.Path.GetExtension(filename);              
   if (String.Compare(ext, ".xaml",true) == 0)
{               
      dataFormat = DataFormats.Xaml;     
            }       
          else if (String.Compare(ext, ".rtf", true) == 0)
{                   
  dataFormat = DataFormats.Rtf;         
        }           
      documentTextRange.Load(stream, dataFormat);       
      }              
  }

8. 将RichTextBox的内容保存为文件:

  

private static void SaveFile(string filename, RichTextBox richTextBox)
        {            
if (string.IsNullOrEmpty(filename))
{                
throw new ArgumentNullException();       
      }
            using (FileStream stream = File.OpenWrite(filename))
{
                TextRange documentTextRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
                string dataFormat = DataFormats.Text;
                string ext = System.IO.Path.GetExtension(filename);
                if (String.Compare(ext, ".xaml", true) == 0)
{
                    dataFormat = DataFormats.Xaml;
                }                
else if (String.Compare(ext, ".rtf", true) == 0)
{
                    dataFormat = DataFormats.Rtf;                
}                 documentTextRange.Save(stream, dataFormat);
            }
        }