Windows Phone - 以WebBrowser為殼做個WebApp
如果您是支持App一定要透過native code來開發的,可以不要往下看,因為對您不適用。
該篇主要介紹如何透過WeBrowser搭配公司既有的Mobile Web做成一個App,
讓小公司如果不想請人開發時,可以運用公司既有的Web技術加上打造一個貌似的App。
話不多說,馬上進入主題。
(1) 建立一個Windows Phone專案,加入必要的<Capabilities />;
‧ID_CAP_WEBBROWSERCOMPONENT
‧ID_CAP_NETWORKING
(2) 加入一個MainPage.xaml清掉畫面中的既有元件,加入<SystemTray />與<WebBrowser />;
xaml檔如下:
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<!--
IsGeolocationEnabled = true,讓HTML 5 Geolocation功能可以使用
IsScriptEnabled = true,啟動JavaScript的功能
-->
<phone:WebBrowser
x:Name="wbObject"
IsGeolocationEnabled="True"
IsScriptEnabled="True"
/>
</Grid>
‧IsGeolocationEnabled = true,讓HTML 5 Geolocation功能可以使用;
‧IsScriptEnabled = true,啟動JavaScript的功能;
(3) 註冊幾個重要的事件與指定要前往的URL:
‧Navigated:監測WebBrowser在URL載入完成後要觸發的事件,例如:關閉ProgressBar;
‧Navigating:監測WebBrowser在URL載入中時的事件,例如:顯示ProgressBar;
‧NavigationFailed:監測當URL載入失敗的事件,例如:顯示錯誤、關閉ProgressBar;
‧ScriptNotify:監測當網頁中如果有相關的Alert或Confimz等JS事件觸發時,可以被通知至App中;
private Uri gURI = new Uri("http://www.plurk.com/m", UriKind.Absolute);
// Constructor
public MainPage()
{
InitializeComponent();
//註冊要監控的事件
wbObject.Navigated += wbObject_Navigated;
wbObject.Navigating += wbObject_Navigating;
wbObject.NavigationFailed += wbObject_NavigationFailed;
wbObject.ScriptNotify += wbObject_ScriptNotify;
//指定要前往的URL
wbObject.Source = gURI;
}
加上分別處理以上註冊事件的任務,包括:開啟/關閉ProgressBar、回應JS事件;
private void wbObject_Navigated(object sender, NavigationEventArgs e)
{
//關閉SystemTray
ControlSystemTray(false, string.Empty);
}
private void wbObject_Navigating(object sender, NavigatingEventArgs e)
{
//開啟SystemTray
ControlSystemTray(true, "loading...");
}
private void wbObject_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
//失敗時也要關閉SystemTray
ControlSystemTray(false, string.Empty);
//顯示錯誤資訊
MessageBox.Show(e.Exception.Message);
}
private void wbObject_ScriptNotify(object sender, NotifyEventArgs e)
{
//回應 e 中的JS呼叫內容
MessageBox.Show(e.Value);
}
private void ControlSystemTray(bool pIsShow, string pMessage)
{
Dispatcher.BeginInvoke(() =>
{
shellProgress.IsVisible = pIsShow;
shellProgress.Text = pMessage;
});
}
以上三個步驟就可以簡單做出個具有貌似App的Web App了。
那麼如果在網頁裡有些事件希望可以跟App進行互動時,該怎麼辦呢?
(4) 註冊對於電話/電郵事件的處理;
參考<Mobile Browser Manipulate Mobile App.>可得知像網頁撥打電話、發送mail,透過location.href或<a href="" />的方式,
但是非常抱歉,在WP版的WebBrowser沒有支援對於tel:、mailto:這種協定進行處理,所以該怎麼解決這樣的問題呢?
根據常見的作法有二種:
(a) 修改既有的網頁,加上JavaScript將識別有<a />標籤的元素,增加onlick()識別是否有tel或mailto,
有的話改用呼叫window.external.Notify的方式;<webbrowser windows phone + tel and mailto tag don't work>
(b) 參考<Windows Phone 7 Web Browser 控制項應用 Part 1>的方式,註冊一段自訂義的JavaScript以取得用戶點擊的href,
再搭配DispatcherTimer定期呼叫自訂義的JavaScript取得href來識別是否有觸發電話或電郵的事件;
整理下來可得知均需透過JavaScript的互動才能夠產生,也代表需要處理 ScriptNotify 事件,往下便藉由 (a) 方法做為範例來說明:
(4-1). 定義要載入的HTML與取代<a/>的JavaScript:
private void ImportHTML()
{
// 定義HTML
string tHtml = @"<html><head></head><body><a href='mailto:xxx@gmail.com'>發送mail</a><br />" +
@"<a href='tel:+88693267111'>撥打電話</a><br />" +
@"<a href='http://www.dotblogs.com.tw/pou'>go to pou's it life</a><br /></body></html>";
// 註冊script,為每一個<a />加上 onclick時要觸發的事件為呼叫window.external.Notify
string notifyJS = @"<script type='text/javascript' language='javascript'>
window.onload = function() {
var links = document.getElementsByTagName('a');
for(var i=0;i<links.length;i++) {
links[i].onclick = function() {
window.external.Notify(this.href);
}
}
}
</script>";
// 將定義好的JavaScript加入至<head />中
tHtml = tHtml.Replace("<head>", string.Format("<head>{0}{1}",
Environment.NewLine, notifyJS));
wbObject.NavigateToString(tHtml);
}
(4-2). 修改ScriptNotify支援不同的href送入的值,例如:http、https、tel、mailto;
private void wbObject_ScriptNotify(object sender, NotifyEventArgs e)
{
//增加處理接受由JavaScript呼叫Notify所傳來的參數
if (e.Value.Contains("http") || e.Value.Contains("https"))
{
wbObject.Navigate(new Uri(e.Value, UriKind.Absolute));
return;
}
if (e.Value.Contains("tel"))
{
//利用PhoneCallTask進行撥打電話
Microsoft.Phone.Tasks.PhoneCallTask tPTask = new Microsoft.Phone.Tasks.PhoneCallTask();
tPTask.PhoneNumber = e.Value.Replace("tel:", "");
tPTask.Show();
return;
}
if (e.Value.Contains("mailto:"))
{
//利用EmailComposeTask進行寄發郵件
Microsoft.Phone.Tasks.EmailComposeTask tMTask = new Microsoft.Phone.Tasks.EmailComposeTask();
tMTask.To = e.Value.Replace("mailto:", "");
tMTask.Show();
return;
}
else
MessageBox.Show(e.Value);
}
要記得宣告「ID_CAP_PHONEDIALER」的Capability。透過ScriptNotify即可完成JavaScript與Native Code溝通的方式,
如果希望可以從網頁呼叫相機功能,也可以可透過這樣的方式來加以進行。
(5) App呼叫網頁中的JavaScript方法;
由App要呼叫網頁的中的JavaScript,該方法與Android是有所不同的,想要呼叫的方法均需要透過「window」做為前綴詞,如下:
「window.{function name}」。如果不是這樣撰寫,那將會收到「An unknown error has occurred. Error: 80020006」的exception。
(5-1). 定義JavaScript的內容,以支援「window.DetectActiveTel」方法提供App呼叫;
private void InvokeJSInPage()
{
// 定義HTML
string tHtml = @"<html><head></head><body><a href='mailto:xxx@gmail.com'>發送mail</a><br />" +
@"<a href='tel:+88693267111'>撥打電話</a><br />" +
@"<a href='http://www.dotblogs.com.tw/pou'>go to pou's it life</a><br /></body></html>";
// 註冊script,為每一個<a />加上 onclick時要觸發的事件為呼叫window.external.Notify
string notifyJS = @"<script type='text/javascript' language='javascript'>
window.DetectActiveTel= function() {
return '123';
};
</script>";
// 將定義好的JavaScript加入至<head />中
tHtml = tHtml.Replace("<head>", string.Format("<head>{0}{1}", Environment.NewLine, notifyJS));
wbObject.NavigateToString(tHtml);
}
(5-2). 定義App呼叫具有「window」前綴詞的JavaScript Function;
private void Button_Click(object sender, RoutedEventArgs e)
{
try
{
//呼叫window.DetectActiveTel= function() {return '123';};
string para = (string)wbObject.InvokeScript("DetectActiveTel");
}
catch (Exception ex)
{
string TMSg = ex.Message;
}
}
[範例程式]
[補充]
提供了與瀏覽器接近的功能,主要便利開發者與Web網頁加以整合。
‧重要的屬性,如下表:
屬性 | 說明 |
CanGoBack | 取得目前WebBrowser是否具有頁面在Browser hitsotry可以進行返回到上一頁。 |
CanGoForward | 取得目前WebBrowser是否具有頁面在Browser hitsotry可以進行前往到下一頁。 |
IsGeolocationEnabled | 開啟/關閉是否允許Website透過WebBrowser取得手機目前Geolocation的資訊。 |
IsScriptEnabled | 開啟/關閉執行scripting。這個設定被指定後會在下一個頁面被載入才會有效,而非目前的頁面。預設是false。 |
Source | 設定/取得WebBrowser的URI location。 |
‧重要方法,如下表:
方法 | 說明 |
GoBack | 強迫WebBrowser返回上一頁。建議在執行前先判斷CanGoBack == true。 |
GoForward | 強迫WebBrowser前往下一頁。建議在執行前先判斷CanGoForward == true。 |
InvokeScript(String) | 指定執行現在被載入頁面中的scripting function。 |
InvokeScript(String, String[]) | 指定執行現在被載入頁面中的scripting function,並且加入傳遞的字串陣列參數。 |
Navigate(Uri) | 啟動導航請求提供的URI。 |
Navigate(Uri, Byte[], String) | 啟動導航請求提供的URI。這種方法允許自定義選項,包括發布的posted form和HTTP headers。 |
NavigateToString | 注入HTML原始碼入WebBrowser來進行呈現。 |
SaveToString | 回傳webpage中的HTML content。 |
ClearCookiesAsync | 非同步清除指定Web browser的相關cookies。(Defined by WebBrowserExtensions.) |
ClearInternetCacheAsync | 非同步清除指定Web browser所緩存的Internet cache associated資料。(Defined by WebBrowserExtensions.) |
GetCookies | 返回從指定的WebBrowser控件的源URI中檢索的Cookie的集合。(Defined by WebBrowserExtensions.) |
‧重要事件,如下表:
事件 | 說明 |
Navigated | 事件發生於WebBrowser成功完成navigaties之後。 |
Navigating | 事件發生於WebBrowser正在進行navigating,包括redirect。 |
NavigationFailed | 事件發生於WebBrowser進行navigate失敗。 |
ScriptNotify | 事件發生當有JavaScript呼叫window.external.notify(<data>) method,例如:alert()…等。 |
======
以上是分享如何透過WebBrowser操作既有的Mobile Web,並且加上一些特殊的處理來完成內鍵Browser無法執行的問題。
更多功能可多了解HTML 5與JS的整合開發與應用,這樣可以讓WebBroser搭配JS做到更多的事情。
References:
‧How to display web content from the network using the WebBrowser control for Windows Phone
‧How to display dynamically generated web content using the WebBrowser control for Windows Phone
‧How to display static web content using the WebBrowser control for Windows Phone
‧How to get and set cookies for Windows Phone
‧How to Utilize Web Browser Control in Windows Phone 7
‧HTML5 Geolocation & HTML <input> Tag & HTML <video> Tag &
‧webbrowser windows phone + tel and mailto tag don't work
‧Windows Phone 7 Web Browser 控制項應用 Part 1
‧Windows Phone 7 Web Browser 控制項應用 Part 2
‧Dynamically add HTML to ASP.NET page
‧ASP.NET利用UserControl做一個MediaPlayer的控制項
‧Media Player ASP.NET Control - C# Source Code
‧如何將檔案上載至 Web 伺服器在 ASP.NET 中,使用視覺 C#.NET