Share via


Understanding Web Browser Control of Windows Phone 7 – Part 2

文 / 黃忠成

 

Gadget Devloping (HTML Base Application)

在多數的 Mobile 系統中,除了原生應用程式的開發平台外,都提供了另一種開發平台,那就是以 HTML 為主的 Mobile Page App 架構,在這種架構下,開發者可以用 HTML+JavaScript 的方式來開發 Mobile Application,像是以往的 Windows Mobile 6/6.5、iPhone 都支援這種開發方式。

會提供這種開發方式的主要原因是,有些開發者對於原生的開發環境及語言並不是那麼熟悉,OS 為了讓這些開發者能以現有的知識來快速開發應用程式,所以才會在原生平台外另闢蹊徑,當然,以 HTML+JavaScript 為基礎的開發方式或許在能力上遠不及原生程式,但若應用程式需求不高,只是要顯示及導覽功能,這樣的開發方式其實效率相當的高。舉個例來說,你發行了一個網路雜誌,每周都能更新,此時在這種以 HTML 為基礎的架構下,妳可以讓美工將雜誌內容編寫為 HTML+JavaScript,然後將其封裝到 XAP 檔中上架到 Marketplace 讓使用者購買並下載。

整個開發流程是相當簡單的,美工編寫 HTML+JavaScript,開發人員將 HTML+JavaScript 封裝到 XAP 檔中,上架販售。

Using Gadget Template for Windows Phone 7

Windows Phone 7 SDK 中並未提供類似於 Gadget 的開發方式,但這不代表我們無法這樣做,因為有了 Web Browser 控制項跟 InvokeScript 機制的幫忙,我們可以輕易的達到同樣的效果,本文末後會提供一個 Gadget Application Tempate 的 Visual Studio 專案範本,讀者們只要下載並照步驟安裝 (後述),即可以類似 Gadget,以 HTML 開發 Windows Phone 7 應用程式。

首先請建立新專案,選擇 WP7GadgetTemplate。

圖1

完成後會看到圖二的方案總管畫面。

圖2

WebContent 目錄下的檔案就是簡單的 HTML+JavaScript,請開啟 index.html,你會發現到其中已有一些預設的內容。

index.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <!--<meta name="viewport" content="width=400, user-scalable=no"/>--> <!--<meta name="viewport" content="width=480, height=80, user-scalable=no,initial-scale=2.5, maximum-scale=5.0, minimum-scale=1.0" />--> <meta name="viewport" content="width=460,user-scalable=no"/> <title> 首頁 </title> <script type="text/javascript" src="wpframework.js"> </script> <!--<script language="javascript" type="text/javascript"> function dial() { var obj = new WP7.Phone(); obj.Dial("39398494", "TEST333"); } function sms() { var obj = new WP7.SMS(); obj.SendSMS("39398494", "TEST333 BODY"); } </script>--> </head> <body> <h2> 歡迎使用 ASP.NET! </h2> <p> 若要進一步了解 ASP.NET,請造訪 <a href="https://www.asp.net" title="ASP.NET 網站">www.asp.net</a>。 </p> <a href="tel: 0999938443">Call 0999938443.</a> <img alt="TEST" src="about.png" /> <script language="javascript" type="text/javascript"> WP7Global.Initialize(); </script> </body> </html>

 

看起來用法跟 HTML+JavaScript 完全相同是吧?接著按下 F5 執行應用程式。

圖3

接著按下 Call 的連結,此時會啟動撥號介面。

圖4

接著修改 index.html 內容,加入 SMS 的 tag。

index.html

…………………. <body> ………. <a href="tel:0999938443">Call 0999938443.</a> <a href="sms:0999938443?Body=Hello WP7">SMS to 0999938443.</a> <img alt="TEST" src="about.png" /> <script language="javascript" type="text/javascript"> WP7Global.Initialize(); </script> </body>

 

按下 F5 後重新執行。

圖5

點選 SMS to …….連結,此時會開出發送簡訊視窗。

圖6

那如果我要加入圖形按鈕呢?也就是在 HTML 中加入一個圖形,讓使用者點選後撥出電話,要怎麼做呢?

很簡單,就照傳統的 HTML 寫法,透過方案總管在 WebContent 資料夾下加入一個圖形檔。

圖7

完成後修改 index.html。

index.html

……….. <a href="tel:339393939"><img alt="Call" src="astrologer.png" /></a> ………..

 

按下 F5 執行。

圖8

點選該圖片可開啟撥號介面。

圖9

很簡單對吧?那如果想要讓使用者點選某個連結後導向另一個 HTML 頁面,又該如何做呢?這更簡單了,只要在 WebContent 資料夾下加入新項目,選擇 WP7HTMLPage 範本。

圖10

按下新增後,即可得到一個全新的 HTML 頁面。接著修改 index.html 內容來添加對此 HTML 頁面的連結。

<a href="WP7HTMLPage1.html">Navigate To ... </a>

 

按下 F5 後執行並點選連結。

圖11

到目前為止,我想你應該已經抓到這個開發模式的重點了。

Gadget Template for Windows Phone 7 安裝

安裝 GadgetTemplate 的方式很簡單,請下載 GadgetTemplate.zip,其內容如下圖。

圖12

將其解壓至 Users\<UserName>\My Documents\Visual Studio 2010 目錄下即可,完成後啟動 Visual Studio 2010 就可看到以上提及的範本。

更多的應用 – 讓使用者輸入電話後啟用撥號介面

index.html

…………… <script language="javascript" type="text/javascript"> function dial() { var obj = new WP7.Phone(); obj.Dial(document.getElementById('tel1').value, "Calling..."); } </script> …………………….. Enter Tel: <input type="text" id="tel1" /> <input type="button" value="Call" onclick="dial()" />

 

圖13

圖14

更多的應用 – 讓使用者輸入電話、簡訊內容後啟用發送簡訊介面

index.html

…………….. <script language="javascript" type="text/javascript"> function dial() { var obj = new WP7.Phone(); obj.Dial(document.getElementById('tel1').value, "Calling..."); } function sendsms() { var obj = new WP7.SMS(); obj.SendSMS(document.getElementById('tel1').value, document.getElementById('smsbody').value); } </script> …………………………… <br /> Enter Tel: <input type="text" id="tel1" /> <input type="button" value="Call" onclick="dial()" /> <br /> Enter SMS Body: <textarea id="smsbody" rows="5" cols="30"></textarea> <input type="button" value="Send Sms" onclick="sendsms()" />

 

圖15

圖16

更多的應用 – 讓使用者由聯絡人選取電話

index.html

…………… <script …….> function chooseContactCallback(phoneNumber) { document.getElementById('tel1').value = phoneNumber; } function chooseContact() { var obj = new WP7.Phone(); obj.ChooseContact("chooseContactCallback"); } </script> ……………… Enter Tel: <input type="text" id="tel1" /> <input type="button" value="Call" onclick="dial()" /> <input type="button" value="..." onclick="chooseContact()" />

 

圖17

圖18

圖19

Inside JavaScript Framework for Windows Phone 7

Gadget Template for Windows Phone 7 所依賴的有兩大機制,一是 Web Browser 控制項的控制,另外就是 JavaScript Framework for Windows Phone 7,在一啟動時,Gadget Template 即會讀入 index.html 並寫到 Isolated Storage 中,因為只有在 Isolated Storage 中的檔案,才能使用以下的程式碼來瀏覽。

webBrowser1.Navigate(new Uri("index.html", UriKind.Relative));

 

當然,你也可以選用另一個函式 NavigateToString,但這樣做還得處理 Encoding 的問題,而且對於 HTML 中連結其他檔案的部分會完全無法處理。

接著,Gadget Template 會使用另一個隱藏的 Web Browser 控制項來載入 index.html,並分析裡面的內容,一併將其中所連結的其它檔案,例如 HTML、Image、JavaScript 一併載入 Isolated Storage 中。

完成這些動作後,Gadget Template 開始真正載入 index.html,並進行初始化動作,這些動作包含了掃描 HTML 中的 tel/sms tag 部份及初始化一些共用變數。

<script language="javascript" type="text/javascript"> WP7Global.Initialize(); </script>

 

當使用者按下 tel/sms 類的連結時,會觸發 ReactiveTelTag 函式 (為什麼?因為 Initialize 會修正這些 Tag 並掛上事件)。

this.ReactiveTelTag = function () { var elem = event.srcElement; if (elem.getAttribute('tel') != null) { window.WP7Global.currentActiveTel = elem.getAttribute('tel'); window.WP7Global.currentActiveName = elem.innerHTML; } else if (elem.getAttribute('smsTel') != null) { window.WP7Global.currentSmsTels = elem.getAttribute('smsTel'); window.WP7Global.currentSmsBody = elem.getAttribute('smsBody'); } else if (elem.parentNode != null) { if (elem.parentNode.getAttribute('tel') != null) { window.WP7Global.currentActiveTel = elem.parentNode.getAttribute('tel'); window.WP7Global.currentActiveName = elem.innerHTML; } else if (elem.parentNode.getAttribute('smsTel') != null) { window.WP7Global.currentSmsTels = elem.parentNode.getAttribute('smsTel'); window.WP7Global.currentSmsBody = elem.parentNode.getAttribute('smsBody'); } } return false; }

 

這裡其實只是設定幾個變數而已,而 MainPage.xaml.cs 中有一個 Timer 每秒偵測這些變數的值。

_timer.Tick += (s, args) => { try { webBrowser1.InvokeScript("DetectStatus"); } catch (Exception) { } };

 

DetectStatus 則是判斷這些變數的值,並編碼後以 window.external.notify 函式回傳給 MainPage.xaml.cs。

function DetectStatus() { if (window.WP7Global.currentActiveTel != "") { var result = "tel" + ";" + window.WP7Global.currentActiveTel + ";" + window.WP7Global.currentActiveName; window.WP7Global.currentActiveTel = ""; window.WP7Global.currentActiveName = ""; window.external.notify(result); } else if (window.WP7Global.currentSmsTels != "") { var result = "sms" + ";" + window.WP7Global.currentSmsTels + ";" + window.WP7Global.currentSmsBody; window.WP7Global.currentSmsTels = ""; window.WP7Global.currentSmsBody = ""; window.external.notify(result); } else if (window.WP7Global.currentCameraActive != "") { var result = "camera" + ";" + window.WP7Global.currentCameraActive; window.WP7Global.currentCameraActive = ""; window.external.notify(result); } else if (window.WP7Global.currentAction != "") { var result = "action;" + window.WP7Global.currentAction + ";" + window.WP7Global.actionCallback; window.WP7Global.currentAction = ""; window.WP7Global.actionCallback = ""; window.external.notify(result); } }

 

於此,我們碰觸到了 Web Browser 另一種讓 HTML 與 Managed Code 互通的機制,那就是 Notify。

Notify 機制的運作過程是,Managed Code 呼叫 InvokeScript,此時被呼叫的函式中可以以 windows.external.notify 函式來發出一個事件,這個事件便是 WebBrowser.NotifyScript。

簡單的說,DetectStatus 呼叫了 window.external.notify,此時 MainPage.xaml.cs 中的 webBrower1 之 ScriptNotify 事件便會觸發。

private void webBrowser1_ScriptNotify(object sender, NotifyEventArgs e) { if (!string.IsNullOrEmpty(e.Value)) { if (e.Value.StartsWith("tel;")) { string[] parameters = e.Value.Substring(4).Split(';'); PhoneCallTask task = new PhoneCallTask(); task.PhoneNumber = parameters[0]; task.DisplayName = parameters[1]; task.Show(); } else if (e.Value.StartsWith("sms;")) { string[] parameters = e.Value.Substring(4).Split(';'); SmsComposeTask task = new SmsComposeTask(); task.To = parameters[0]; task.Body = e.Value.Substring(4 + parameters[0].Length + 1); if (task.Body != string.Empty) task.Body = task.Body.Split('=')[1]; task.Show(); } else if (e.Value.StartsWith("camera;")) { string[] parameters = e.Value.Substring(7).Split(';'); CameraCaptureTask task = new CameraCaptureTask(); task.Show(); task.Completed += (s1, args1) => { }; } else if (e.Value.StartsWith("action;")) { string[] parameters = e.Value.Substring(7).Split(';'); if (parameters[0] == "chooseContact") { PhoneNumberChooserTask task = new PhoneNumberChooserTask(); task.Completed += (s, args) => { webBrowser1.InvokeScript(parameters[1], args.PhoneNumber); }; task.Show(); } } } }

 

於此我們對 JavaScript 所發出的內容作出反應,例如撥號、傳簡訊、選取聯絡人,都是用這種方法達到的。

ScriptNotify or not?

你應該會開始迷惑,因為在 Part 1 時,我們並沒有使用 Notify 機制,而是採用偵測變數改變的方式,這是因為 Notify 機制無法運作於以 eval 注入 JavaScript 的模式,此時便只能使用 Part 1 的方式了。

Gadget Template的限制

Gadget Template 不支援子目錄,也就是說,所以相關的 HTML、圖片、JavaScript 都得放在 WebContent 目錄下。

另外,Gadget Template 目前僅支援 C# 語言 (指的是,Gadget Template 本身是以 C# 開發,而相關的範本也都是 C# 版)。

 

[GadgetTemplate & Samples 下載]

http://cid-1e6d55012e5efedb.office.live.com/self.aspx/%e5%85%ac%e9%96%8b/WP7%5E_UWP/GadgetTemplateWithSample.ZIP