一個WinForm富文本編輯器控件

WinForm 上的富文本編輯器簡直不要太少,雖然有 RichEdit,但是這個鬼極難用而且復(fù)雜,在插入圖片和表格的時候簡直抓狂,還要理解復(fù)雜的 RTF 格式。

我希望有一個文本控件,包括基本的格式設(shè)置,圖片表格插入等,能夠自定義打開文件,保存和插入圖片等功能,并且它的依賴項要盡可能少,因為是 WinForm 控件,也不用需要跨 Linux 和 Osx 平臺,只用在 Windows 下保持兼容就行。這么一來,似乎并沒有好的免費控件可用了 ...

但是,Js 的這類控件就比比皆是了,有沒有辦法移到 C# WinForm 上來用呢,答案當(dāng)然是 YES!

首先要顯示 HTML頁面和JS的執(zhí)行,必須要由 WebBrowser 控件承載,所以我們的整個編輯器都會在 WebBrowser 中呈現(xiàn)。接下來是編輯器控件了,盡量輕量級,最好是美觀點,文檔全面,接口豐富的,我找到我用過的一款。

summernote https://github.com/summernote/summernote/

接下來是編輯器的界面了,創(chuàng)建一個 HTML 頁面,呈現(xiàn)編輯器,并設(shè)置編譯方式為嵌入的資源,將所有的腳本文件內(nèi)容全部和樣式內(nèi)容寫在這個 HTML 頁面中,這樣一來,頁面可能達到驚人的幾百 KB,不過這沒關(guān)系,除了腳本之外,還會有字體資源,關(guān)于字體資源如何嵌入在 CSS 中,可以通過下列方式:

@font-face {
    font-family: "name";
    font-style: normal;
    font-weight: normal;
    src: url(data:font/truetype;charset=utf-8;base64,XXX);
}

需要注意的是 WebBrowser 是 IE 核心,所以只需要 eot 格式的字體即可。關(guān)于如何將字體生出你個 Base64 字符串,猛擊 http://www.motobit.com/util/base64-decoder-encoder.asp

編輯器的事件,我們寫成接口,由調(diào)用方自行實,分別是保存按鈕、打開文件按鈕、插入圖片按鈕,異常等事件。

 public interface IKBrowserEventListener
 {
     void onSaveClicked();
     void onOpenFileClicked();
     void onInsertImageClicked();
     void onError(Exception ex);
 }

如何直接使用 WebBrowser 控件的話,會有一些奇怪的問題,比如阻止腳本錯誤執(zhí)行的對話框依舊會執(zhí)行 ... 可是直接使用 COM+ 接口 IWebBrowser2,需要引用 Microsoft Internet Controls。

public class KBrowser : System.Windows.Forms.WebBrowser
{
    private SHDocVw.IWebBrowser2 Iwb2;

    public KBrowser()
    {
        NewWindow += KBrowser_NewWindow;
    }

    private void KBrowser_NewWindow(object sender, System.ComponentModel.CancelEventArgs e)
    {
        KBrowser kb = sender as KBrowser;
        string url = kb.StatusText;
        Navigate(url);
        e.Cancel = true;
    }

    protected override void AttachInterfaces(object nativeActiveXObject)
    {
        Iwb2 = (SHDocVw.IWebBrowser2)nativeActiveXObject;
        Iwb2.Silent = true;
        base.AttachInterfaces(nativeActiveXObject);
    }

    protected override void DetachInterfaces()
    {
        Iwb2 = null;
        base.DetachInterfaces();
    } 
}

接下來編輯器控件可以用用戶控件,拖一個 KBrowser即可,Dock 為 Fill 鋪滿整個控件。它至少擁有下列屬性:

/// <summary>
/// 編輯器的事件監(jiān)聽器
/// </summary>
public IKBrowserEventListener KBrowserEventListener { get; set; }
/// <summary>
/// 獲取或設(shè)置編輯器中Html值
/// </summary>
public string Html
{
    get
    {
        try
        {
            return kBrowser1.Document.InvokeScript("getHtml", null).ToString();
        }
        catch (Exception ex)
        {
            onError(ex);
            return "";
        }
    }
    set
    {
        try
        {
            kBrowser1.Document.InvokeScript("setHtml", new string[] { value });
        }
        catch (Exception ex)
        {
            onError(ex);
        }
    }
}

在這個 Html 的屬性中,包括了 JS 和 C# 的互調(diào)用代碼,這里是在 C# 中調(diào)用 JS 的一個方法,并且一個有返回值但無參數(shù),一個有參數(shù)但無返回值。

如果在 JS 里調(diào)用 C#,需要將類設(shè)置為 ComVisible(true),應(yīng)用到方法級不知是否也可以,沒試過。然后 window.external.XXX() 的方式調(diào)用,XXX 是 C# 的方法。有沒有參看重載就知道了。

在編輯器控件 OnLoad 時加載 Html 編輯器,因為是嵌入的資源,所以不是通過 File IO 的方式。

 private void KEditor_Load(object sender, EventArgs e)
 {
     try
     {
         Stream sm = Assembly.GetExecutingAssembly().GetManifestResourceStream("Knote.Widgets.Resources.editor.html");
         byte[] bs = new byte[sm.Length];
         sm.Read(bs, 0, (int)sm.Length);
         sm.Close();
         UTF8Encoding con = new UTF8Encoding();
         string str = con.GetString(bs);
         kBrowser1.DocumentText = str;
     }
     catch (Exception ex)
     {
         onError(ex);
     }
 }

然后添加一些方法供 JS 調(diào)用,基本就是上述接口中的方法,調(diào)用前一定要判斷是否空指針。

/// <summary>
/// 保存按鈕點擊的事件,請不要調(diào)用,而是使用監(jiān)聽器
/// </summary>
public void onSaveButtonClick()
{
    if (KBrowserEventListener != null)
        KBrowserEventListener.onSaveClicked();
}

/// <summary>
/// 打開文件按鈕點擊的事件,請不要調(diào)用,而是使用監(jiān)聽器
/// </summary>
public void onOpenFileButtonClick()
{
    if (KBrowserEventListener != null)
        KBrowserEventListener.onOpenFileClicked();
}

/// <summary>
/// 插入圖片按鈕點擊的事件,請不要調(diào)用,而是使用監(jiān)聽器
/// </summary>
public void onInsertPictureButtonClick()
{
    if (KBrowserEventListener != null)
        KBrowserEventListener.onInsertImageClicked();
}

插入普通文本和插入 HTML 源代碼。

/// <summary>
/// 插入一個節(jié)點,它將由 div 元素包裹
/// </summary>
/// <param name="html"></param>
public void InsertNode(string html)
{
    try
    {
        kBrowser1.Document.InvokeScript("insertNode", new string[] { html });
    }
    catch (Exception ex)
    {
        onError(ex);
    }
}

/// <summary>
/// 插入文本
/// </summary>
/// <param name="text"></param>
public void InsertText(string text)
{
    try
    {
        kBrowser1.Document.InvokeScript("insertText", new string[] { text });
    }
    catch (Exception ex)
    {
        onError(ex);
    }
}

為什么是 insertText 和 insertNode,這個是 JS 控件決定的,知道流程后,就可以封裝任意編輯器了,最終完成效果如下,并且設(shè)計階段也是所見即所得。

WinForm 編輯器

封裝后只有一個 DLL,地址 https://github.com/yahch/kwig

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容