最近一个需求是好友的创意,实现类似Word域功能的RichTextBox,以实现自定义模版功能。域是Word中最具有实用价值的功能之一,它表示文档中可能发生变化的数据或邮件合并文档中套用信函、标签中的占位符。在我们实现的RTF版本中,将域定义为一个封闭的字符串,并将这个具有域的RTF内容称之为”模板”。


我们的具体需求为: 1、自定义一个模拟Word域的封闭字符串,即域占位符,我们简单的将占位符定义为[…]; 2、允许从外部拖动一个代表域的控件到RichTextBox中,自动生成域占位符; 3、在RTF格式化的文本中查找这个域占位符; 4、允许从SQL Server数据库中存取具有域的RTF内容; 5、将域与相应数据库表的字段关联起来,用字段值替换域占位符;
首先,RTF编码中可能含有单引号,而我们又不得不使用SQL Server 2008,这时就出现了数据库字段值的单引号问题。在我们的版本中使用加密字符串对此来进行处理,最大限度避免单引号问题。
加密方法的定义如下,具体方法的内容详见附带源码:
private string Decrypt(String strText, String sDecrKey)  
public string Encrypt(string stringToEncrypt, string SEncryptionKey)
定义两个数据库表Table1和Table2,分别保存域对应的值和RTF编码,数据库结构详见附带源码中的数据库备份,大致如下:
Table1的字段: name nchar sex nchar age nchar
Table2的字段: rtf ntext plaintext ntext
保存rtf编码的代码如下:
//获取rtf
private void button1_Click(object sender, EventArgs e)
{
    try
    {
        SqlConnection conn = new SqlConnection(textBox1.Text);
        SqlCommand sqlcmd = new SqlCommand();
        sqlcmd.Connection = conn;
        conn.Open();
        sqlcmd.CommandText = "select top 1 * from table2";
        SqlDataAdapter dbDA = new SqlDataAdapter();
        dbDA.SelectCommand = sqlcmd;
        DataSet ds = new DataSet();
        dbDA.Fill(ds);
        richTextBox1.Rtf = Decrypt(ds.Tables[0].Rows[0]["rtf"].ToString(), "&%#@?,:*");
    }
    catch //(Exception e)
    {}
    finally
    {}
}
//保存rtf and plaintext
private void button4_Click(object sender, EventArgs e)
{
    try
    {
        SqlConnection conn = new SqlConnection(textBox1.Text);
        SqlCommand sqlcmd = new SqlCommand();
        sqlcmd.Connection = conn;
        conn.Open();
        sqlcmd.CommandText = "select top 1 * from table2";
        SqlDataAdapter dbDA = new SqlDataAdapter();
        SqlParameter bjSqlParameter = new SqlParameter(); 
        SqlCommand updateCommand = new SqlCommand("UPDATE table2 SET rtf = @rtf, plaintxt = @plaintxt", conn);
        dbDA.UpdateCommand =updateCommand;
        objSqlParameter  = updateCommand.Parameters.Add("@rtf", SqlDbType.NText, 1000, "rtf");
        objSqlParameter.SourceColumn = "rtf";
        objSqlParameter.SourceVersion = DataRowVersion.Current;
        bjSqlParameter = updateCommand.Parameters.Add("@plaintxt", SqlDbType.NText, 1000, "plaintxt");
        objSqlParameter.SourceColumn = "plaintxt";
        objSqlParameter.SourceVersion = DataRowVersion.Current; 
        dbDA.SelectCommand = sqlcmd;
        DataSet ds = new DataSet();
        dbDA.Fill(ds,"rtftable");
        DataTable dbTable = ds.Tables[0];
        DataRow dbRow = dbTable.Rows[0];
        dbRow["rtf"] = Encrypt(richTextBox1.Rtf.ToString(), "&%#@?,:*");
        dbRow["plaintxt"] = richTextBox1.Text;
        dbDA.Update(ds, "rtftable");   
    }
    catch //(Exception e)
    { }
    finally
    { }
}
将控件拖动到RichtextBox中的实现在.Net下也是相当的简单,只要对待拖动的控件的MouseDown事件进行处理并传递需要的字符串即可:
private void button2_MouseDown(object sender, MouseEventArgs e)
{
    button2.DoDragDrop(button2.Tag.ToString(), DragDropEffects.All);
}
RichTextBox控件自身具备接受拖动字符串的功能,所以不必专为RichTextBox创建接受拖动的处理。具体请详见博文附带源码。
该示例在 Windows XP SP3 + Visual Studio 2008 SP1 + SQL Server 2008 下编译调试成功。
