Windows Phone 7 Web Browser 控制項應用 Part 2

在多數的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 下載

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