天天看點

「幹貨分享」DevExpress常用控件——RichEditControl使用指南

做WinForms的一般都知道,傳統.NET界面有一個RichTextBox控件,這個是一個富文本控件,可以存儲圖檔文字等内容,它有自己的檔案格式RTF,在DevExpress控件組裡面也有一個同等的控件,他的名字是RichEditControl,這個控件功能很強大,它可以做郵件編輯器,實作圖文并茂的郵件的功能,如下所示。本文主要介紹如何一步步使用這個控件實作自己需要的功能和界面。

「幹貨分享」DevExpress常用控件——RichEditControl使用指南

DevExpress WinForms能完美建構流暢、美觀且易于使用的應用程式,無論是Office風格的界面,還是分析處理大批量的業務資料,它都能輕松勝任!

複制下面連結擷取工具(PC端):https://www.evget.com/product/740/download

但是預設它沒有任何工具欄,全部是需要自己添加上去。

如何建立帶工具欄的RichEditControl控件

為了使得控件更加通用,我做了一個自定義控件,用來實作通用文本編輯器的功能,首先我們建立一個自定義控件,如下所示。

「幹貨分享」DevExpress常用控件——RichEditControl使用指南

這樣我們會看到一個空白的自定義控件界面,然後再往裡面添加一個RichEditControl進去,設定Dock=Fill,讓RichEditControl控件鋪滿整個自定義控件界面,如下所示。

設定器ActiveViewType=Simple,讓控件顯示的更緊湊一些。如下所示。

「幹貨分享」DevExpress常用控件——RichEditControl使用指南

從上面我們看到,它預設是沒有任何工具欄的,比較簡單,那麼我們要添加向上面郵件界面的功能,如何實作呢?很簡單,選中RichEditControl,然後再右上角的三角符号上,單擊可以看到有一些功能菜單,如下所示。

「幹貨分享」DevExpress常用控件——RichEditControl使用指南

單擊Create BarManager然後可以進一步看到更多的工具欄菜單了,如下所示。你可以懸着Create All Bar來建立所有工具欄,然後删除多餘的就可以了。

「幹貨分享」DevExpress常用控件——RichEditControl使用指南

這樣就可以把所有的工具欄全部列出來了,很多很多。

「幹貨分享」DevExpress常用控件——RichEditControl使用指南

但是一般我們不需要那麼多,精簡一些重要的功能即可,這些多餘的最好删除,否則很淩亂。

這些功能按鈕預設都已經帶有事件的處理,就是不需要額外的代碼就可以實作各種标準的功能了,這些很友善,類似DevExpress這方面做得很好,如列印預覽控件也是一樣,基本上不需要編寫代碼了,選擇需要的功能,多餘的删除即可,這樣就可以精簡到我本文開頭的那個郵件編輯器界面了。

「幹貨分享」DevExpress常用控件——RichEditControl使用指南

如何實作自定義的按鈕功能

剛才說到,裡面的按鈕可以随意删除,也可以随意組合到一個工具欄裡面,當然,這個控件的工具欄除了内置的按鈕外,還可以增加自己的按鈕和事件相應,這樣就更加完美和強大了。

如上面的按鈕,就是我用來截圖的一個功能,自定義的,内置的沒有這樣的功能,這樣添加按鈕及圖檔後,實作按鈕的事件就可以了,和自己建立的按鈕一樣。

這個截圖的功能,利用了我的共用類庫裡面的一個截圖類,實作代碼如下所示。

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);
}
}           

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

發送成功後,我們就會看到圖文并茂的郵件了。

「幹貨分享」DevExpress常用控件——RichEditControl使用指南

3. 内容支援搜尋的操作

郵件儲存的時候,我們可以把RichEditControl的内容作為RTF格式進行儲存,這樣圖檔的資源就作為代碼進行儲存了,這樣恢複顯示也很友善,唯一不好的就是這些内容不好搜尋,建議如果要支援搜尋,可以通過增加另外一個文本字段,把内容轉換為純文字進行儲存,這樣加載就以RTF内容加載,搜尋的時候,就搜尋純文字就可以了。

以上就是我在使用DevExpress的RichEditControl過程中碰到和解決問題的過程,希望對大家有幫助,共同提高。

本文轉載自:部落格園 - 伍華聰

繼續閱讀