做Winform的,我們一般都知道,傳統.NET界面有一個RichTextBox控件,這個是一個富文本控件,可以存儲圖片文字等內容,它有自己的文件格式RTF,在DevExpress控件組里面也有一個同等的控件,他的名字是RichEditControl,這個控件功能很強大,在我上一篇隨筆《Winform開發框架之通用短信郵件通知模塊》中,有介紹過利用它來做郵件編輯器,實現圖文並茂的郵件的功能,如下所示。本文主要介紹如何一步步使用這個控件實現自己需要的功能和界面。
但是默認它沒有任何工具欄,全部是需要自己添加上去。
1、如何創建帶工具欄的RichEditControl控件
為了使得控件更加通用,我做了一個自定義控件,用來實現通用文本編輯器的功能,首先我們創建一個自定義控件,如下所示。
這樣我們會看到一個空白的自定義控件界面,然后再往里面添加一個RichEditControl進去,設置Dock=Fill,讓RichEditControl控件鋪滿整個自定義控件界面,如下所示。
設置器ActiveViewType=Simple,讓控件顯示的更緊湊一些。如下所示。
從上面我們看到,它默認是沒有任何工具欄的,比較簡單,那么我們要添加向上面郵件界面的功能,如何實現呢?很簡單,選中RichEditControl,然后再右上角的三角符號上,單擊可以看到有一些功能菜單,如下所示。
單擊Create BarManager然后可以進一步看到更多的工具欄菜單了,如下所示。你可以懸着Create All Bar來創建所有工具欄,然后刪除多余的就可以了。
這樣就可以把所有的工具欄全部列出來了,很多很多。
但是一般我們不需要那么多,精簡一些重要的功能即可,這些多余的最好刪除,否則很凌亂。
這些功能按鈕默認都已經帶有事件的處理,就是不需要額外的代碼就可以實現各種標准的功能了,這些很方便,類似DevExpress這方面做得很好,如打印預覽控件也是一樣,基本上不需要編寫代碼了,選擇需要的功能,多余的刪除即可,這樣就可以精簡到我本文開頭的那個郵件編輯器界面了。
2、如何實現自定義的按鈕功能
剛才說到,里面的按鈕可以隨意刪除,也可以隨意組合到一個工具欄里面,當然,這個控件的工具欄除了內置的按鈕外,還可以增加自己的按鈕和事件相應,這樣就更加完美和強大了。
如上面的按鈕,就是我用來截圖的一個功能,自定義的,內置的沒有這樣的功能,這樣添加按鈕及圖片后,實現按鈕的事件就可以了,和自己創建的按鈕一樣。
這個截圖的功能,利用了我的共用類庫里面的一個截圖類,實現代碼如下所示。
ScreenCaptureWindow captureWindow = null; private void barCapture_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { if (this.ParentForm.Owner != null) { this.ParentForm.Owner.Hide(); } this.ParentForm.Hide(); if (captureWindow != null) { captureWindow.BitmapCropped -= new EventHandler(captureWindow_BitmapCropped); captureWindow.Dispose(); captureWindow = null; } if (captureWindow == null) { captureWindow = new ScreenCaptureWindow(); captureWindow.BitmapCropped += new EventHandler(captureWindow_BitmapCropped); } captureWindow.Show(); captureWindow.TopMost = true; captureWindow.TopMost = false; } void captureWindow_BitmapCropped(object sender, EventArgs e) { try { if (captureWindow.DragStop != captureWindow.DragStart) { RichEditControl control = this.richEditControl1; control.Document.InsertImage(control.Document.CaretPosition, DocumentImageSource.FromImage(captureWindow.BitmapCache)); } } finally { if (this.ParentForm.Owner != null) { this.ParentForm.Owner.Show(); } this.ParentForm.Show(); } }
這個截圖,直接就是把Image插入到RichEditControl里面,不需要另外存儲圖片到文件里的,這就是RichEditControl控件方便之處,如果我們要實現插入圖片和加載文檔的方法,除了使用內置按鈕(推薦)外,其實自己也可以寫事件來實現的,如下代碼就是實現這兩個簡單的功能(一般不需要)。
private void barInsertImg_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { string selectImage = FileDialogHelper.OpenImage(true, ""); if (!string.IsNullOrEmpty(selectImage)) { foreach (string file in selectImage.Split(new char[] { ',', ';', ',', ';' })) { if (File.Exists(file)) { try { RichEditControl control = this.richEditControl1; control.Document.InsertImage(control.Document.CaretPosition, DocumentImageSource.FromFile(file)); } catch { } } } } } private void barLoadFile_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { string filter = "Word2003(*.doc)|*.doc|Word2007(*.docx)|*.docx|RTF(*.rtf)|*.rtf|HTM(*.htm)|*.htm|HTML(*.html)|*.html|All File(*.*)|*.*"; string file = FileDialogHelper.Open("打開文件", filter); if (!string.IsNullOrEmpty(file)) { //string htmlContent = File.ReadAllText(file, Encoding.Default); //this.richEditControl1.HtmlText = htmlContent; string path = Path.GetFullPath(file); string extension = Path.GetExtension(file); switch (extension.ToLower()) { case ".htm": case ".html": this.richEditControl1.Document.LoadDocument(file, DocumentFormat.Html, path); break; case ".doc": this.richEditControl1.Document.LoadDocument(file, DocumentFormat.Doc, path); break; case ".docx": this.richEditControl1.Document.LoadDocument(file, DocumentFormat.OpenXml, path); break; case ".rtf": this.richEditControl1.Document.LoadDocument(file, DocumentFormat.Rtf, path); break; default: this.richEditControl1.Document.LoadDocument(file, DocumentFormat.PlainText, path); break; } //DocumentRange range = richEditControl1.Document.Range; //CharacterProperties cp = this.richEditControl1.Document.BeginUpdateCharacters(range); //cp.FontName = "新宋體"; //cp.FontSize = 12; //this.richEditControl1.Document.EndUpdateCharacters(cp); } }
3、RichEditControl的特殊操作
1)文檔字體修正
RichEditControl控件功能是強大,不過一般也需要處理一些特殊的情況,由於該控件加載的時候,默認好像字體都是方正姚體的字體,因此感覺很不好看,那么我們就要在文檔加載的時候,把它的字體修改下,操作如下所示,修改為新宋體的字體比方正姚體的好看很多。
public MyRichEdit() { InitializeComponent(); this.richEditControl1.DocumentLoaded += new EventHandler(richEditControl1_DocumentLoaded); } void richEditControl1_DocumentLoaded(object sender, EventArgs e) { DocumentRange range = richEditControl1.Document.Range; CharacterProperties cp = this.richEditControl1.Document.BeginUpdateCharacters(range); cp.FontName = "新宋體"; //cp.FontSize = 12; this.richEditControl1.Document.EndUpdateCharacters(cp); }
2)RichEditControl內置圖片資源的解析
RichEditControl控件支持把圖片作為內嵌資源存儲在里面,如果我們要把他作為郵件發送,我們知道,郵件內容雖然是HTML的,但是圖片資源需要獨立取出來放到LinkedResource對象作為郵件發送才能顯示,否則不能顯示圖片的。而RichEditControl默認轉換出來的HTML內容,是把圖片作為Base64碼寫到文檔里面,文檔比較大的。為了實現把圖片獨立提取出來,我們需要一個該控件的解析類RichMailExporter,代碼如下所示。
/// <summary> /// 把RichEditControl里面的內容導出為HTML和嵌入圖片資源的輔助函數 /// </summary> public class RichMailExporter : IUriProvider { int imageId; readonly RichEditControl control; List<LinkedAttachementInfo> attachments; public RichMailExporter(RichEditControl control) { Guard.ArgumentNotNull(control, "control"); this.control = control; } /// <summary> /// 導出內容和嵌入資源 /// </summary> /// <param name="htmlBody">HTML內容</param> /// <param name="attach">附件資源</param> public virtual void Export(out string htmlBody, out List<LinkedAttachementInfo> attach) { this.attachments = new List<LinkedAttachementInfo>(); control.BeforeExport += OnBeforeExport; htmlBody = control.Document.GetHtmlText(control.Document.Range, this); control.BeforeExport -= OnBeforeExport; attach = this.attachments; } void OnBeforeExport(object sender, BeforeExportEventArgs e) { HtmlDocumentExporterOptions options = e.Options as HtmlDocumentExporterOptions; if (options != null) { options.Encoding = Encoding.UTF8; } } #region IUriProvider Members public string CreateCssUri(string rootUri, string styleText, string relativeUri) { return String.Empty; } public string CreateImageUri(string rootUri, RichEditImage image, string relativeUri) { string imageName = String.Format("image{0}", imageId); imageId++; RichEditImageFormat imageFormat = GetActualImageFormat(image.RawFormat); Stream stream = new MemoryStream(image.GetImageBytes(imageFormat)); string mediaContentType = RichEditImage.GetContentType(imageFormat); LinkedAttachementInfo info = new LinkedAttachementInfo(stream, mediaContentType, imageName); attachments.Add(info); return "cid:" + imageName; } private RichEditImageFormat GetActualImageFormat(RichEditImageFormat _RichEditImageFormat) { if (_RichEditImageFormat == RichEditImageFormat.Exif || _RichEditImageFormat == RichEditImageFormat.MemoryBmp) return RichEditImageFormat.Png; else return _RichEditImageFormat; } #endregion }
/// <summary> /// 用來傳遞附件信息 /// </summary> [Serializable] public class LinkedAttachementInfo { private Stream stream; private string mimeType; private string contentId; /// <summary> /// 參數構造函數 /// </summary> /// <param name="stream">附件流內容</param> /// <param name="mimeType">附件類型</param> /// <param name="contentId">內容ID</param> public LinkedAttachementInfo(Stream stream, string mimeType, string contentId) { this.stream = stream; this.mimeType = mimeType; this.contentId = contentId; } /// <summary> /// 附件流內容 /// </summary>public Stream Stream { get { return stream; } } /// <summary> /// 附件類型 /// </summary>public string MimeType { get { return mimeType; } } /// <summary> /// 內容ID /// </summary>public string ContentId { get { return contentId; } } }
這樣我們在獲取編輯控件里面的HTML的同時,也把里面的附件提取出來了,方便我們發送郵件使用,如下代碼就是獲取HTML內容和附件列表的,其中LinkedAttachementInfo是以Stream和相關信息存在的一個實體類。
string html = ""; List<LinkedAttachementInfo> linkList = new List<LinkedAttachementInfo>(); RichMailExporter exporter = new RichMailExporter(this.txtBody.richEditControl1); exporter.Export(out html, out linkList); MailInfo info = new MailInfo(); info.Subject = this.txtSubject.Text; info.Body = html; info.IsBodyHtml = true; info.EmbedObjects.AddRange(linkList);//添加嵌入資源 info.ToEmail = this.txtRecipient.Text;//收件人
這樣我們發送郵件的時候,寫入附件數據就可以了,如下代碼所示。
//嵌入資源的發送操作 AlternateView view = AlternateView.CreateAlternateViewFromString(mailInfo.Body, Encoding.UTF8, MediaTypeNames.Text.Html); foreach (LinkedAttachementInfo link in mailInfo.EmbedObjects) { LinkedResource resource = new LinkedResource(link.Stream, link.MimeType); resource.ContentId = link.ContentId; view.LinkedResources.Add(resource); } mail.AlternateViews.Add(view);
發送成功后,我們就會看到圖文並茂的郵件了。
3、內容支持搜索的操作
郵件保存的時候,我們可以把RichEditControl的內容作為RTF格式進行保存,這樣圖片的資源就作為代碼進行保存了,這樣恢復顯示也很方便,唯一不好的就是這些內容不好搜索,建議如果要支持搜索,可以通過增加另外一個文本字段,把內容轉換為純文本進行保存,這樣加載就以RTF內容加載,搜索的時候,就搜索純文本就可以了。
以上就是我在使用DevExpress的RichEditControl過程中碰到和解決問題的過程,希望對大家有幫助,共同提高。
如果需要了解DevExpress控件使用的一些文章,可以看看我的這幾篇隨筆。
《Winform傳統DataGridView和DevExpress控件的GridControl兩者表頭全選功能的實現(源碼提供)》