使用Razor範本建置HTML檢視

在行動裝置開發世界中,「混合式應用程式」一詞通常是指在託管 Web 查看器控制件內,將部分(或全部)畫面呈現為 HTML 頁面的應用程式。

有一些開發環境可讓您完全在 HTML 和 JavaScript 中建置行動應用程式,不過,這些應用程式在嘗試完成複雜的處理或 UI 效果時,可能會遭受效能問題,而且在可存取的平臺功能中也會受到限制。

Xamarin 提供兩個世界的最佳功能,特別是在使用Razor HTML 範本化引擎時。 使用 Xamarin 時,您可以彈性地建置使用 JavaScript 和 CSS 的跨平台樣板化 HTML 檢視,但也能夠完整存取基礎平臺 API,並使用 C# 快速處理。

本文件說明如何使用Razor範本檔化引擎來建置HTML+JavaScript+CSS 檢視,這些檢視可使用 Xamarin 跨行動平臺使用。

以程序設計方式使用 Web 檢視

在瞭解Razor之前,本節將說明如何使用Web檢視直接顯示HTML內容,特別是應用程式內產生的HTML內容。

Xamarin 提供 iOS 和 Android 上基礎平臺 API 的完整存取權,因此使用 C# 輕鬆建立及顯示 HTML。 每個平臺的基本語法如下所示。

iOS

在 Xamarin.iOS 的 UIWebView 控件中顯示 HTML 也只需要幾行程式代碼:

var webView = new UIWebView (View.Bounds);
View.AddSubview(webView);
string contentDirectoryPath = Path.Combine (NSBundle.MainBundle.BundlePath, "Content/");
var html = "<html><h1>Hello</h1><p>World</p></html>";
webView.LoadHtmlString(html, NSBundle.MainBundle.BundleUrl);

如需使用UIWebView控件的詳細資訊,請參閱iOS UIWebView配方。

Android

使用 Xamarin.Android 在 WebView 控件中顯示 HTML,只需幾行程式代碼即可完成:

// webView is declared in an AXML layout file
var webView = FindViewById<WebView> (Resource.Id.webView);

// enable JavaScript execution in your html view so you can provide "alerts" and other js
webView.SetWebChromeClient(new WebChromeClient());

var html = "<html><h1>Hello</h1><p>World</p></html>";
webView.LoadDataWithBaseURL("file:///android_asset/", html, "text/html", "UTF-8", null);

如需使用 WebView 控件的詳細資訊,請參閱 Android WebView 配方。

指定基底目錄

這兩個平臺上都有一個參數,指定 HTML 頁面的基底目錄。 這是裝置文件系統上的位置,用來解析影像和 CSS 檔案等資源的相對參考。 例如,標籤類似

<link rel="stylesheet" href="style.css" />
<img src="monkey.jpg" />
<script type="text/javascript" src="jscript.js">

請參閱這些檔案: style.cssmonkey.jpgjscript.js。 基底目錄設定會告知 Web 檢視這些檔案的位置,以便將其載入頁面。

iOS

範本輸出會以下列 C# 程式代碼在 iOS 中轉譯:

webView.LoadHtmlString (page, NSBundle.MainBundle.BundleUrl);

基底目錄會指定為 NSBundle.MainBundle.BundleUrl ,其是指應用程式安裝所在的目錄。 Resources 資料夾中的所有檔案都會複製到此位置,例如這裡顯示的style.css檔案:

iPhoneHybrid solution

所有靜態內容檔案的建置動作應該是 BundleResource

iOS project build action: BundleResource

Android

當 Html 字串顯示在網頁檢視中時,Android 也需要以參數的形式傳遞基底目錄。

webView.LoadDataWithBaseURL("file:///android_asset/", page, "text/html", "UTF-8", null);

特殊字串 file:///android_asset/ 是指應用程式中的 Android Assets 資料夾,如下所示,其中包含 style.css 檔案。

AndroidHybrid solution

所有靜態內容檔案的建置動作應該是 AndroidAsset

Android project build action: AndroidAsset

從 HTML 和 JavaScript 呼叫 C#

當 HTML 頁面載入網頁檢視時,它會將連結和表單視為從伺服器載入頁面時。 這表示如果使用者按兩下連結或提交表單,Web 檢視會嘗試瀏覽至指定的目標。

如果連結是外部伺服器(例如 google.com),則網頁檢視會嘗試載入外部網站(假設有因特網連線)。

<a href="http://google.com/">Google</a>

如果鏈接是相對的,則 Web 檢視會嘗試從基底目錄載入該內容。 這顯然不需要網路連線才能運作,因為內容會儲存在裝置上的應用程式中。

<a href="somepage.html">Local content</a>

表單動作遵循相同的規則。

<form method="get" action="http://google.com/"></form>
<form method="get" action="somepage.html"></form>

您不會在用戶端上裝載網頁伺服器;不過,您可以使用現今回應式設計模式中使用的相同伺服器通訊技術,透過 HTTP GET 呼叫服務,併發出 JavaScript 以異步方式處理回應(或呼叫已裝載於 Web 檢視中的 JavaScript)。 這可讓您輕鬆地將數據從 HTML 傳回 C# 程式代碼,以便進行處理,然後在 HTML 頁面上顯示結果。

iOS 和 Android 都提供應用程式程式代碼攔截這些流覽事件的機制,讓應用程式程式代碼可以回應(如有需要)。 這項功能對於建置混合式應用程式非常重要,因為它可讓機器碼與 Web 檢視互動。

iOS

可以覆寫 iOS 網頁檢視上的 ShouldStartLoad 事件,以允許應用程式程式代碼處理流覽要求(例如按兩下連結)。 方法參數會提供所有資訊

bool HandleShouldStartLoad (UIWebView webView, NSUrlRequest request, UIWebViewNavigationType navigationType) {
    // return true if handled in code
    // return false to let the web view follow the link
}

然後指定事件處理程式:

webView.ShouldStartLoad += HandleShouldStartLoad;

Android

在 Android 上,只需子類別 WebViewClient,然後實作程式代碼以響應流覽要求。

class HybridWebViewClient : WebViewClient {
    public override bool ShouldOverrideUrlLoading (WebView webView, IWebResourceRequest request) {
        // return true if handled in code
        // return false to let the web view follow the link
    }
}

然後在 Web 檢視上設定用戶端:

webView.SetWebViewClient (new HybridWebViewClient ());

從 C 呼叫 JavaScript#

除了告訴網頁檢視載入新的 HTML 頁面之外,C# 程式代碼也可以在目前顯示的頁面內執行 JavaScript。 整個 JavaScript 程式代碼區塊可以使用 C# 字串建立並執行,或者您可以透過 script 標記製作對頁面上已可用的 JavaScript 方法呼叫。

Android

建立要執行的 JavaScript 程式代碼,然後使用 “javascript:” 作為前置詞,並指示 Web 檢視載入該字串:

var js = "alert('test');";
webView.LoadUrl ("javascript:" + js);

iOS

iOS Web 檢視提供特別呼叫 JavaScript 的方法:

var js = "alert('test');";
webView.EvaluateJavascript (js);

摘要

本節介紹 Android 和 iOS 上 Web 檢視控件的功能,讓我們使用 Xamarin 建置混合式應用程式,包括:

  • 從程式代碼中產生的字串載入 HTML 的能力,
  • 參考本機檔案的能力(CSS、JavaScript、影像或其他 HTML 檔案)
  • 在 C# 程式代碼中攔截流覽要求的能力,
  • 能夠從 C# 程式代碼呼叫 JavaScript。

下一節介紹Razor,可讓您輕鬆地建立HTML以在混合式應用程式中使用。

什麼是 Razor?

Razor 是一種範本化引擎,由 ASP.NET MVC 引進,原本是在伺服器上執行,併產生要提供給網頁瀏覽器的 HTML。

Razor 範本化引擎會使用 C# 擴充標準 HTML 語法,以便輕鬆表達配置並併入 CSS 樣式表單和 JavaScript。 範本可以參考 Model 類別,它可以是任何自定義類型,而且其屬性可以直接從範本存取。 其主要優點之一是能夠輕鬆地混合 HTML 和 C# 語法。

Razor 範本不限於伺服器端使用,也可以包含在 Xamarin 應用程式中。 使用Razor範本以及以程序設計方式使用Web檢視的功能,可讓複雜的跨平臺混合式應用程式使用 Xamarin 來建置。

Razor 樣本基本概念

Razor 範本檔案具有 .cshtml 擴展名。 您可以從 [新增檔案] 對話框中的 [文字範本化] 區段,將這些專案新增至 Xamarin 專案:

New File - Razor Template

簡單的Razor樣本 ( RazorView.cshtml) 如下所示。

@model string
<html>
    <body>
    <h1>@Model</h1>
    </body>
</html>

請注意下列與一般 HTML 檔案的差異:

  • 符號 @ 在 Razor 範本中具有特殊意義 ,表示要評估下列運算式為 C#。
  • @model 指示詞一律會顯示為 Razor 範本檔案的第一行。
  • 指示 @model 詞後面應該接著 Type。 在此範例中,會將簡單的字串傳遞至範本,但這可能是任何自定義類別。
  • @Model 在整個範本中被參考時,它會提供在產生範本時傳遞至範本的物件參考(在此範例中為字串)。
  • IDE 會自動為範本產生部分類別(擴展名為 .cshtml檔案)。 您可以檢視此程序代碼,但不應該加以編輯。 RazorView.cshtml 部分類別名為 RazorView,以符合 .cshtml 範本檔名。 這是這個名稱,用來參考 C# 程式代碼中的範本。
  • @using 語句也可以包含在Razor範本頂端,以包含其他命名空間。

最後的 HTML 輸出接著可以使用下列 C# 程式代碼產生。 請注意,我們將 Model 指定為字串 「Hello World」,此字串會併入轉譯的範本輸出中。

var template = new RazorView () { Model = "Hello World" };
var page = template.GenerateString ();

以下是 iOS 模擬器和 Android 模擬器上 Web 檢視中顯示的輸出:

Hello World

更多 Razor 語法

在本節中,我們將介紹一些基本的Razor語法,以協助您開始使用它。 本節中的範例會以數據填入下列類別,並使用 Razor 加以顯示:

public class Monkey {
    public string Name { get; set; }
    public DateTime Birthday { get; set; }
    public List<string> FavoriteFoods { get; set; }
}

所有範例都會使用下列數據初始化程序代碼

var animal = new Monkey {
    Name = "Rupert",
    Birthday=new DateTime(2011, 04, 01),
    FavoriteFoods = new List<string>
        {"Bananas", "Banana Split", "Banana Smoothie"}
};

顯示模型屬性

當模型是具有屬性的類別時,可以輕鬆地在Razor樣本中參考它們,如下列範例範本所示:

@model Monkey
<html>
    <body>
    <h1>@Model.Name</h1>
    <p>Birthday: @(Model.Birthday.ToString("d MMMM yyyy"))</p>
    </body>
</html>

這可以使用下列程式代碼轉譯為字串:

var template = new RazorView () { Model = animal };
var page = template.GenerateString ();

最終輸出會顯示在 iOS 模擬器和 Android 模擬器的 Web 檢視中:

Rupert

C# 陳述式

範本中可以包含更複雜的 C#,例如 Model 屬性更新和此範例中的 Age 計算:

@model Monkey
<html>
    <body>
    @{
        Model.Name = "Rupert X. Monkey";
        Model.Birthday = new DateTime(2011,3,1);
    }
    <h1>@Model.Name</h1>
    <p>Birthday: @Model.Birthday.ToString("d MMMM yyyy")</p>
    <p>Age: @(Math.Floor(DateTime.Now.Date.Subtract (Model.Birthday.Date).TotalDays/365)) years old</p>
    </body>
</html>

您可以撰寫複雜的單行 C# 運算式(例如格式化年齡),方法是使用 @()來圍繞程序代碼。

您可以使用 來撰寫 @{}多個 C# 語句。

If-else 語句

程序代碼分支可以使用 來表示, @if 如此範本範例所示。

@model Monkey
<html>
    <body>
    <h1>@Model.Name</h1>
    <p>Birthday: @(Model.Birthday.ToString("d MMMM yyyy"))</p>
    <p>Age: @(Math.Floor(DateTime.Now.Date.Subtract (Model.Birthday.Date).TotalDays/365)) years old</p>
    <p>Favorite Foods:</p>
    @if (Model.FavoriteFoods.Count == 0) {
        <p>No favorites</p>
    } else {
        <p>@Model.FavoriteFoods.Count favorites</p>
    }
    </body>
</html>

迴圈

您也可以新增類似 的 foreach 迴圈建構。 前置 @ 詞可用於迴圈變數 ( @food 在此案例中) 以 HTML 轉譯。

@model Monkey
<html>
    <body>
    <h1>@Model.Name</h1>
    <p>Birthday: @Model.Birthday.ToString("d MMMM yyyy")</p>
    <p>Age: @(Math.Floor(DateTime.Now.Date.Subtract (Model.Birthday.Date).TotalDays/365)) years old</p>
    <p>Favorite Foods:</p>
    @if (Model.FavoriteFoods.Count == 0) {
        <p>No favorites</p>
    } else {
        <ul>
            @foreach (var food in @Model.FavoriteFoods) {
                <li>@food</li>
            }
        </ul>
    }
    </body>
</html>

上述樣本的輸出會顯示在 iOS 模擬器和 Android 模擬器上執行:

Rupert X Monkey

本節涵蓋使用 Razor 範本來轉譯簡單唯讀檢視的基本概念。 下一節說明如何使用 Razor 建置更完整的應用程式,以在 HTML 檢視和 C# 中接受 JavaScript 之間的使用者輸入和互操作。

搭配 Xamarin 使用 Razor 範本

本節說明如何使用 Visual Studio for Mac 中的解決方案範本來建置您自己的混合式應用程式。 [檔案新增>方案...] 視窗中有三個可用的>範本:

  • Android 應用程式 > Android > WebView 應用程式
  • iOS > 應用程式 > WebView 應用程式
  • ASP.NET MVC專案

針對 i 電話 和 Android 專案,[新增方案] 視窗看起來像這樣- 右側的解決方案描述會醒目提示 Razor 範本化引擎的支援。

Creating iPhone and Android solutions

請注意,您可以輕鬆地將 .cshtml Razor 範本新增任何現有的 Xamarin 專案,因此不需要使用這些解決方案範本。 iOS 專案不需要分鏡腳本使用Razor;只要以程式設計方式將 UIWebView 控件新增至任何檢視,您就可以在 C# 程式代碼中轉譯整個 Razor 範本。

i 電話 與 Android 專案的預設樣本解決方案內容如下所示:

iPhone and Android templates

這些範本提供您現成的應用程式基礎結構,以使用數據模型物件載入 Razor 範本、處理使用者輸入,並透過 JavaScript 與用戶通訊。

解決方案的重要部分包括:

  • 靜態內容, 例如style.css 檔案。
  • Razor .cshtml 範本檔案,例如 RazorView.cshtml
  • Razor 樣本中所參考的模型類別,例如 ExampleModel.cs
  • 建立 Web 檢視並轉譯樣本的平臺特定類別,例如 MainActivity Android 上的 與 iPhoneHybridViewController iOS 上的 。

下一節說明項目的運作方式。

靜態內容

靜態內容包括 CSS 樣式表單、影像、JavaScript 檔案或其他內容,這些內容可以從 Web 檢視中顯示的 HTML 檔案連結或參考。

範本專案包含最小的樣式表單,以示範如何在混合式應用程式中包含靜態內容。 範本中會參考 CSS 樣式表單,如下所示:

<link rel="stylesheet" href="style.css" />

您可以新增所需的任何樣式表單和 JavaScript 檔案,包括 JQuery 之類的架構。

Razor cshtml 範本

此範本包含 Razor .cshtml 檔案,其中包含預先撰寫的程式代碼,可協助在 HTML/JavaScript 與 C# 之間通訊數據。 這可讓您建置複雜的混合式應用程式,而不只是顯示來自模型的唯讀數據,還接受 HTML 中的使用者輸入,並將它傳回 C# 程式代碼進行處理或儲存。

轉譯範本

GenerateString在範本上呼叫 會呈現 HTML 準備好在網頁檢視中顯示。 如果範本使用模型,則應該在轉譯之前提供它。 此圖表說明轉譯的運作方式,而不是使用提供的基底目錄來尋找指定的檔案,由 Web 檢視解析靜態資源。

Razor flowchart

從範本呼叫 C# 程式代碼

透過設定 Web 檢視的 URL,然後攔截 C# 中的要求來處理原生要求,而不需要重載 Web 檢視,即可從轉譯的 Web 檢視進行回呼的 Web 檢視進行通訊。

您可以在如何處理 RazorView 的按鈕中看到一個範例。 按鈕具有下列 HTML:

<input type="button" name="UpdateLabel" value="Click" onclick="InvokeCSharpWithFormValues(this)" />

JavaScript 函 InvokeCSharpWithFormValues 式會從 HTML 窗體讀取所有值,並設定 location.href 網頁檢視的 :

location.href = "hybrid:" + elm.name + "?" + qs;

這會嘗試使用自訂設定將網頁檢視巡覽至 URL(例如 hybrid:

hybrid:UpdateLabel?textbox=SomeValue&UpdateLabel=Click

當原生 Web 檢視處理此流覽要求時,我們有機會攔截它。 在 iOS 中,這是藉由處理 UIWebView 的 HandleShouldStartLoad 事件來完成。 在 Android 中,我們只會將表單中使用的 WebViewClient 子類別化,並覆寫 ShouldOverrideUrlLoading。

這兩個瀏覽攔截器的內部基本上相同。

首先,檢查 Web 檢視嘗試載入的 URL,如果它不是以自訂配置 (hybrid:開頭,則允許流覽正常發生。

針對自定義 URL 配置,配置與 “?” 之間 URL 的所有專案 是要處理的方法名稱(在此案例中為 “UpdateLabel” )。 查詢字串中的所有內容都會被視為方法呼叫的參數:

var resources = url.Substring(scheme.Length).Split('?');
var method = resources [0];
var parameters = System.Web.HttpUtility.ParseQueryString(resources[1]);

UpdateLabel 在此範例中,在文本框參數上執行最少的字串操作量(在字串前面加上 “C# 說”),然後呼叫回 Web 檢視。

處理 URL 之後,方法會中止流覽,讓 Web 檢視不會嘗試完成流覽至自定義 URL。

從 C 操作範本#

從 C# 對轉譯的 HTML Web 檢視進行通訊,是在 Web 檢視中呼叫 JavaScript 來完成。 在 iOS 上,這是藉由在 UIWebView 上呼叫 EvaluateJavascript 來完成:

webView.EvaluateJavascript (js);

在 Android 上,您可以使用 URL 配置,將 JavaScript 載入為 URL,以在 Web 檢視中叫用 "javascript:" JavaScript:

webView.LoadUrl ("javascript:" + js);

讓應用程式真正混合

這些範本不會在每個平臺上使用原生控件 , 整個畫面會填入單一網頁檢視。

HTML 非常適合原型設計,並顯示網頁最擅長的種類,例如 RTF 和回應式版面配置。 不過,並非所有工作都適合 HTML 和 JavaScript – 捲動長的數據清單,例如,使用原生 UI 控制項執行得更好(例如 iOS 上的 UITableView 或 Android 上的 ListView)。

範本中的 Web 檢視可以輕鬆地透過平臺特定控件來增強 ,只要在 Mac 上使用 Xcode 編輯 MainStoryboard.storyboard ,或在 Android 上使用 Resources/layout/Main.axml 即可。

RazorTodo 範例

RazorTodo 存放庫包含兩個不同的解決方案,可顯示完全 HTML 驅動應用程式與結合 HTML 與原生控件的應用程式之間的差異:

  • RazorTodo - 使用 Razor 範本的完整 HTML 驅動應用程式。
  • RazorNativeTodo - 使用 iOS 和 Android 的原生清單檢視控件,但使用 HTML 和 Razor 顯示編輯畫面。

這些 Xamarin 應用程式會在 iOS 和 Android 上執行,利用可攜式類別庫 (PCL) 來共用常見的程式代碼,例如資料庫和模型類別。 Razor .cshtml 範本也可以包含在 PCL 中,以便輕鬆地跨平台共用。

這兩個範例應用程式都納入來自原生平臺的 Twitter 共用和文字到語音轉換 API,示範搭配 Xamarin 的混合式應用程式仍然可以從 HTML Razor 範本驅動檢視存取所有基礎功能。

RazorTodo 應用程式會針對清單和編輯檢視使用 HTML Razor 範本。 這表示我們幾乎可以在共用的可攜式類別庫中建置應用程式(包括資料庫和 .cshtml Razor 範本)。 下列螢幕快照顯示iOS和Android應用程式。

RazorTodo

RazorNativeTodo 應用程式會針對編輯檢視使用 HTML Razor 範本,但在每個平台上實作原生卷動清單。 這提供一些優點,包括:

  • 效能 - 原生卷動控件會使用虛擬化來確保即使有很長的數據清單,也能快速且順暢地捲動。
  • 原生體驗 - 平臺特定的UI元素很容易啟用,例如iOS和Android中的快速捲動索引支援。

使用 Xamarin 建置混合式應用程式的主要優點是,您可以從完全 HTML 驅動的使用者介面(例如第一個範例)開始,然後視需要新增平臺特定功能(如第二個範例所示)。 iOS 和 Android 上的原生清單畫面和 HTML Razor 編輯畫面如下所示。

RazorNativeTodo

摘要

本文說明 iOS 和 Android 上可用的 Web 檢視控件功能,有助於建置混合式應用程式。

然後,它討論了Razor範本化引擎和語法,可用來使用輕鬆地在 Xamarin 應用程式中產生 HTML。cshtml Razor 範本檔案。 它也描述 Visual Studio for Mac 解決方案範本,可讓您快速開始使用 Xamarin 建置混合式應用程式。

最後,它引進了RazorTodo範例,示範如何結合Web檢視與原生使用者介面和API。