[食譜好菜] CefSharp 把 Google Chrome 的核心 Chromium 變成了 WinForms/WPF 的控制項

UI(User Interface)是人機系統當中相當重要的部分,由於現代軟體的呈現模式非常多元,從傳統的 Desktop,到 Web、App,甚至 AR、MR、...等等,以致於一個開發者要通透所有 UI 設計工具幾乎是不可能的,所以當我們設計好一套 UI 之後,能夠有個工具幫忙照搬到其他的開發框架上是最好不過的,本文要介紹的 CefSharp 是一套可以將我們寫好的網頁,直接搬到 WinForms/WPF 上使用的套件,想要開發 Windows 視窗應用程式,身邊卻只有會網頁設計的開發人員可以配合設計 UI 時,CefSharp 就可以幫助我們把網頁設計師開發好的網頁,無縫地直接搬到 WinForms/WPF 上使用,省時又省力。

CefSharp 是基於 Chromium Embedded Framework(CEF)所開發出來的 .NET 套件,它將 Chromium 瀏覽器封裝成一個 WinForms/WPF 的控制項,直接跟 WinForms/WPF 整合在一起,雖然微軟自家有 Microsoft Edge WebView2,但是目前使用上還是有一些問題有待完善(參考:WebView2 使用及現狀),而且 CefSharp 還有一個吸引我的地方,就是我用 .NET Core 開發好獨立部署出去之後,無論使用者的電腦是 Windows PC,還是 Windows Server,主流版本幾乎都可以點兩下直接執行就好。

起手式

首先先透過 NuGet 把 CefSharp 安裝起來,它幾乎是跟著 Chromium 版本更新的,使用上也很簡單,我以 .NET Core 3.1 WinForms 專案為範例,只要三行程式碼就可以把 Chromium 瀏覽器加進來,並且開啟想要瀏覽的網頁。

public Form1()
{
    this.InitializeComponent();

    var browser = new ChromiumWebBrowser { Dock = DockStyle.Fill };

    browser.LoadUrl("https://html5test.com/");

    this.Controls.Add(browser);
}

載入自己開發的網頁檔

把 Chromium 瀏覽器內嵌進 Windows 視窗應用程式,主要當然不是為了要瀏覽線上某個網址,而是要來瀏覽我們自己開發的網頁檔,而且如果是正式的專案通常網頁不會只有一頁,下面我要來介紹兩種載入網頁檔的方式。

1. 內建的 FolderSchemeHandlerFactory

第一種方式是使用 CefSharp 內建的 FolderSchemeHandlerFactory,我們將網頁檔加進專案之後,接著編輯專案檔,直接指定網頁檔資料夾底下的所有檔案為建置的內容,並且永遠複製到輸出目錄。

接著我們在 Program.cs 加入一段程式碼,用來自訂 Scheme 資訊,其中比較特別的是 SchemeNameDomainName 我們也可以自訂,最後 SchemeHandlerFactory 的部分我們就指定為 FolderSchemeHandlerFactory。

[STAThread]
static void Main()
{
    var settings = new CefSettings();

    settings.RegisterScheme(
        new CefCustomScheme
        {
            SchemeName = "chef",
            DomainName = "appcookhouse",
            SchemeHandlerFactory = new FolderSchemeHandlerFactory(@"Views", defaultPage: "index.html")
        });

    Cef.Initialize(settings);

    //...
}

然後在呼叫 browser.LoadUrl() 的時候,參數改傳入 chef://appcookhouse/,我們指定的預設頁面就被載入了。

而我們指定的網頁檔資料夾就成了網站的根目錄,我們完全可以使用相對路徑來連結資料夾內的其他檔案。

2. 實作 ISchemeHandlerFactory 自訂 SchemeHandlerFactory

如果我們想把我們的網頁弄成內嵌資源,而不是以檔案的方式存在,那麼瀏覽路徑的檔案對應我們就得自製 SchemeHandlerFactory 來處理,我把網頁檔資料夾內的檔案全部指定為內嵌資源後,建立了一個 ResourcesSchemeHandlerFactory 並且實作 ISchemeHandlerFactory

public class ResourcesSchemeHandlerFactory : ISchemeHandlerFactory
{
    public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
    {
        var fileName = new Uri(request.Url).AbsolutePath;

        if (fileName == "/") fileName = "/index.html";

        // 網址轉換為資源名稱
        var resourceName = Regex.Replace(fileName.Replace("/", "."), "\\.(\\d)", "._$1");

        // 從內嵌資源讀取網頁檔
        var resource = Assembly.GetExecutingAssembly().GetManifestResourceStream($"NetCoreCefSharpLab.Views{resourceName}");

        if (resource != null)
        {
            var fileExtension = Path.GetExtension(fileName);

            fileExtension = string.IsNullOrEmpty(fileExtension) ? ".html" : fileExtension;

            return ResourceHandler.FromStream(resource, mimeType: Cef.GetMimeType(fileExtension));
        }

        return null;
    }
}

接著將 SchemeHandlerFactory 指定為 ResourcesSchemeHandlerFactory,此時網頁就改從內嵌資源載入。

CefSharp 的基本使用方式就先介紹到這邊,官方有一篇 General Usage 值得看一看,相當於是 CefSharp 的使用指南,希望對大家有一點幫助。

相關資源

C# 指南
ASP.NET 教學
ASP.NET MVC 指引
Azure SQL Database 教學
SQL Server 教學
Xamarin.Forms 教學