在多數的Mobile系統中,除了原生應用程式的開發平台外,都提供了另一種開發平台,那就是以HTML為主的Mobile Page App架構,在這種架構下,
開發者可以用HTML+JavaScript的方式來開發Mobile Application,像是以往的Windows Mobile 6/6.5、iPhone都支援這種開發方式。
Understanding Web Browser Control of Windows Phone 7 – Part 2
文/黃忠成
Gadget Developing (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 |
<!DOCTYPEhtml 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,請造訪<ahref="http://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 下載