Javascript Metro Style App (4) 利用 Web API 建置 Metro Style App Grid Application Service

在這篇會用最快的方式去建置提供 Metro Style App Grid Application 資料的 Web API

根據 Metro Style App 預設的 json 格式建立 ViewModel

public class MetroStyleApp
{
public class Group
{
public string key { get; set; }
public string title { get; set; }
public string subtitle { get; set; }
public string backgroundImage { get; set; }
public string description { get; set; }
public IEnumerable<Item> items { get; set; }
}

public class Item
{
public string title { get; set; }
public string subtitle { get; set; }
public string content { get; set; }
public string backgroundImage { get; set; }
public string description { get; set; }
public Group group { get; set; }
}
}

✕這邊要自訂名稱也是可以,不過在 Metro Style App Grid Application 裡面就要去修改相對應的 javascript 了

建立資料庫

Group

id int
name nvarchar
subTitle nvarchar
title nvarchar
backgroundImage nvarchar
description nvarchar

Item

id int
groupId int
subTitle nvarchar
title nvarchar
article nvarchar
backgroundImage nvarchar
description nvarchar

✕其實資料庫怎麼建都沒有關係,只要能轉型成為 ViewModel 格式就OK了,這邊只是個人的作法

將資料庫資料轉為 ViewModel 輸出

搭配 Entity Framework & Value Injecter

public IEnumerable<MetroStyleApp.Item> Get()
{
NearByEntities db = new NearByEntities();
List<Item> dblist = db.Items.ToList();
List<MetroStyleApp.Item> itemList =
dblist.Select(it => new MetroStyleApp.Item()
.InjectFrom(it, new
{
group = (MetroStyleApp.Group)new MetroStyleApp.Group()
.InjectFrom(it.group, new { key = it.group.name })
,
content = it.article
}
))
.Cast<MetroStyleApp.Item>().ToList();
return itemList;
}

Metro Style App Grid Application 欄位顯示區塊對應

在群組頁面顯示情況下

image

在內容頁面顯示情況之下

image
這邊的 Content 是可以接受 Html 語法的


MVC4 多了什麼新東西 – Web API
Metro Style App (1) 建立 Javascript Metro Style Project
ASP.NET C# MVC ViewModel with valueinjecter

Javascript Metro Style App (3) 設定資料來源

設定完 Metro App 基本資料之後,接著來設定 App 顯示的資料來源

本機資料來源

開啟專案目錄下的 js > data.js

var groupDescription = "...";
var itemDescription = "...";
var itemContent = "...";

// These three strings encode placeholder images. You will want to set the
// backgroundImage property in your real data to be URLs to images.
var lightGray = "...";
var mediumGray = "...";
var darkGray = "...";

// Each of these sample groups must have a unique key to be displayed
// separately.
var sampleGroups = [
...
];

// Each of these sample items should have a reference to a particular
// group.
var sampleItems = [
...
];

這邊一整段都是資料的部分,可以看的出來都是純 json 的資料型態,如果要設定本機資料可以直接在這邊修改,或是比較建議的做法是另外開一個 data 的資料夾來存放這些 json 的資料。

遠端資料來源

如果 APP 本身資料是從遠端接收的 client-server 架構,也可以直接從遠端路徑讀取需要的 json 資料,原本的 Local 資料也就是 json 物件,所以如果要遠端資料就只要能回傳 json 物件就可以根據內容去顯示。這邊也可以自行修改 javascript 來改變顯示的方式,如果是還不想碰到 javascript 的話,就依照原本的資料結構也可以顯示內容。

微軟給的範例連結

找到 data.js 底下修改

sampleItems.forEach(function (item) {
list.push(item);
});

成為

WinJS.xhr({url: "http://contosorecipes8.blob.core.windows.net/AzureRecipes"})
.then(function (xhr) {
var items = JSON.parse(xhr.responseText);

// Add the items to the WinJS.Binding.List
items.forEach(function (item) {
list.push(item);
});
});

再重新啟動之後就可以看到官方的範例內容。

image

有加上圖片內容之後整體質感會差很多,其實這也是 Metro UI 的特色,讓內容來決定 App ,看到的東西就是真正的內容,而減少為了編排或是版面設計出來的圖片。讓整體介面變得以內容為主。

怎麼判斷裝置的瀏覽器尺寸

不同的屬性

  • clientHeight
    • 完整可以顯示的高度
  • offsetHeight
    • 包含瀏覽器的 padding 就是那些邊框
  • scrollHeight
    • 包含邊框還有滾軸展開的高度

在行動裝置下取得尺寸

<html>
<head>
<title>取得尺寸</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no,minimum-scale=1.0,maximum-scale=1.0" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script>
$(document).ready(function () {
$("#spanClientHeight").text(document.documentElement.clientHeight);
$("#spanClientWidth").text(document.documentElement.clientWidth);
});
</script>
</head>
<body>
<div>
高度:<span id="spanClientHeight"></span>
<br />
寬度:<span id="spanClientWidth"></span>
</div>
</body>
</html>

這邊取得到的會是行動裝置解析度的寬度,而不是真實寬度。

image

利用 ViewPort 取得真實寬度

<meta name="viewport" 
content="width=device-width, initial-scale=1.0,
user-scalable=no,minimum-scale=1.0,maximum-scale=1.0" />

設定 viewport 之後瀏覽器就不是根據解析度去顯示而是實際螢幕大小

image

Demo 頁面

連結

image


clientHeight/clientWidth returning different values on different browsers

CSS / JavaScript – How do you get the rendered height of an element?

element.clientHeight – MDN

WCF & WebAPI 序列化 Dictionary 物件到 Json 的格式問題

最近同事遇到一個問題,在 WCF 跟 ASP.NET MVC 4 的 WebAPI 下回傳 Dictionary 的物件的時候和 Json.net 序列化的結果不同,回傳的格式也不是那麼方便使用。

WCF & WebAPI 回傳 Dictionary 的格式

利用 Web API 建立

public object Get()
{
Dictionary<string, object> dic = new Dictionary<string, object>();
dic.Add("key1", "value1");
dic.Add("key2", "value2");
return dic;
}

他回傳的格式會是

[
{
Key: "key1",
Value: "value1"
},
{
Key: "key2",
Value: "value2"
}
]

但是這樣子回傳的 Json 物件在 Javascript 下並不方便直接使用,沒辦法用 object.key1 直接取值,或是 object[“key1”] 也沒辦法。比較理想的回傳格式應該是

{
"key1": "value1",
"key2": "value2"
}

解決方法

這個問題是 WCF & Web API 再回傳的時候序列化元件的邏輯處理的,其實嚴格來說並不是 Bug ,只是不好用而已,當然有其他幾種方法來處理。

  1. 直接組字串
  2. 回傳的時候用 Json.NET 序列化之後再回傳

這兩種都不是很好的做法,有點失去 WCF & Web API 原本的用意。

第三種作法

利用自訂的 class 去改變序列化的邏輯,在這邊我們建立

[Serializable]
public class JsonDictionary : ISerializable
{
Dictionary<string, object> dict = new Dictionary<string, object>();
public JsonDictionary() { }
public JsonDictionary(Dictionary<string, object> dic)
{
this.dict = dic;
}
protected JsonDictionary(SerializationInfo info, StreamingContext context)
{
foreach (var entry in info)
{
dict.Add(entry.Name, entry.Value);
}
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
foreach (string key in dict.Keys)
{
info.AddValue(key, dict[key], dict[key] == null ? typeof(object) : dict[key].GetType());
}
}

public void Add(string key, object value)
{
dict.Add(key, value);
}

public static explicit operator JsonDictionary(Dictionary<string, object> dic)
{
return new JsonDictionary(dic);
}
}

改變 GetObjectData 用以取代原先序列化的邏輯,並且加上 explicit operator 方便跟 Dictionary 做轉型的動作。

改寫之後的 Web API

public object Get()
{
Dictionary<string, object> dic = new Dictionary<string, object>();
dic.Add("key1", "value1");
dic.Add("key2", "value2");
return (JsonDictionary)dic;
}

在這邊再回傳的時候先轉型為剛剛自訂的 JsonDictionary ,可以發現的是一開始我就設定回傳型別為 object ,只是為了讓這個方法看起來比較好用一些。回傳前轉型回傳型態也是要跟著改變的。

{
"key1": "value1",
"key2": "value2"
}

這樣回傳的時候就會是想要的格式了。


JSON deserializer does not deserialize a Dictionary correctly

c# – .NET WCF Json deserializing a Dictionary<int, int> – Stack

c#WCF – POST JSON Dictionary without Key/Value Text – Stack

c# – Make ASP.NET WCF convert dictionary to JSON, omitting “Key

WCF JSON Serialization is flawed « Duncan Smart’s Weblog

.NET by Example: JSON services revisited: using a Dictionary as a

Javascript Metro Style App (2) 設定 package.appxmanifest

只有空殼的專案當然是不行的,接下來開始對整個專案進行最基本的客製化設定。開啟專案底下的 package.appxmanifest ,在這邊有幾個關於 APP 的設定。
image

Application UI

  • 顯示名稱
  • 敘述
  • 起始頁面
  • Logo 圖片
  • APP 支援的瀏覽模式

Metro App 對於圖片都有制式規格,為了讓整個 App 可以融入 Metro UI 的環境

  • Logo 150*150 (Metor UI 的方塊圖)
  • Wide Logo 310*150 (Metro UI 下使用者可以自行放大成長方圖)
  • Small Logo 30*30
  • Badge Logo 24*24
  • Splash Screen 620*300 (進入 APP Loading 顯示)

Capabilities

這邊主要是設定這個 APP 對於裝置上的功能,例如說本機音樂檔案播放、相機功能。
image

Declarations

這邊也是跟系統功能有關的部分,例如說請求成為放入 DVD 的時候的預設播放軟體。
image
image

Content Urls

設定 APP 在網路上的內容資料來源
image

Packaging

APP 本身的發佈資訊
image


Unable to load Package.appxmanifest on Visual – Ronald Widha
What is Package.appxmanifest File in Windows 8 Metro Style App

Javascript Metro Style App (1) 建立 Javascript Metro Style Project

Windows 8 在底層加入了 WinRT API 讓 Javascript 可以透過這個介面去存取本機的資源。也就是說只要利用 Html + CSS + Javascript 就可以開發 Windows 8 App。有點類似於 PhoneGap 的作用,只是現在變成官方架構。不只如此,在 Visual Studio 11 Beta 也加入了專案範本,幫助開始建立 Javascript 的 Metro Style App
win8-new-platform

基本開發環境

  • Windows 8 Consumer Preview
  • Visual Studio 11 Beta

建立 Metro 專案

File > New > Project
Templates > Other Languages > Javascript > Windows Metro Style
必須是安裝在 Windows 8 Consumer Preview 上面的 Visual Studio 11 Beta 才能找到這個選項,安裝在 Windows 7 上面的是沒有的。
image

接下來先選擇 Grid Application 作為第一個專案

image

第一次執行 Javascript Metro App

先來看看執行畫面,直接啟動 Debugging(F5)
image
這個並不是瀏覽器的畫面,而是透過 WinRT 來顯示。也可以點擊進入 Detail 頁面
image
如果選擇的是 Group Title
image

專案架構

image

  • html
    • groupedItemsPage.html – 起始頁面
    • itemDetailPage.html – Item Detail 頁面
    • groupDetailPage.html – Group Detail 頁面
  • js
    • data.js – 裡面有一些簡單的資料用來產生範本
    • default.js – default.html 的 code-behind
    • groupDetailPage.js – groupDetailPage.html 的 code-behind
    • groupedItemsPage.js- groupedItemsPage.html 的 code-behind
    • itemDetailPage.js- itemDetailPage.html 的 code-behind
    • navigator.js – 控制頁面之間的行為

MVC4 多了什麼新東西 – Single Page Application (SPA)

ASP.NET MVC4 Beta 除了 Web API 也同樣新增了另一個專案範本,Single Page Application (SPA),為什麼會有只要一頁的專案,那應該只是拿來測試的吧,有必要特地新增一個專案範本嗎。
image
原來這邊的 Single Page 指的是用單一頁面完成整個網站的瀏覽,利用 javascript 直接建立起 Client 的系統,再透過 javascript 來處理 Request & Response ,好處是可以讓網頁動作更流暢.也可以結合 Web API 的概念,用同樣的 controller 同時對不同 Client 支援(當然 client 還是要分好幾套來寫),這樣想起來其實 SPA 根本就應該跟 Web API 放一起就好了啊。
spa.clientstack
建立的專案其實重點就是在 javascript 上面,這邊 .net 跟 Visual Studio 能提供的支援就少了很多了,但是我覺得將來漸漸這種類型的網站會愈來愈多,更重視在使用者互動上。
image


Steve Sanderson’s Knockout Blog

利用 ThemeRoller 設計版面

themroller-mobile-logo
我是到前一陣子才發現 ThemeRoller 這個好用到一個不行的工具,調整版面對我來說實在是一個很痛苦的事情,又是哪個東西改個顏色、改個寬度、改個配色,東修西改一半的開發時間都花在這上面了。如果有要用 jQuery Mobile 來開發的人,強烈建議一開始就使用 ThemeRoller
image
一開始進入頁面就可以看到這個編輯器,預設會先有三個空白的 theme 可以做設定,而左側功能列就是有相關的選項可以做選擇,可以看到基本模組都是可以做調整的。在設定完之後只要按下左上方的 Download Theme 就可以做匯出的動作,include 的方法也非常簡單,匯出的時候就有說明了。
image
在右上方輸入 Theme 的自訂名稱,並且在下載之後加入專案當中,也要在頁面中引用,要在 jQuery Mobile 的 js 跟 css 之前。不過有用過  jQuery Mobile 原本就有預設五種 Theme 可以使用,在官網也有介紹,當一加入 ThemeRoller 產生出來的 css 檔案之後,原本的 Theme 效果就會被覆蓋掉了,如果說想要保留原本的預設樣式要怎麼做呢?幸好還有另外一個 import 功能。
image
選擇右上角的 Import Default Theme 就可以載入 jQuery Mobile 預設的範本,不用害怕會覆蓋掉現在已經在運作的 jQuery Mobile ,或是說如果想要預設的樣式還要自己設定,如果已經有 ThemeRoller 匯出的檔案也只要複製 css 直接匯入就可以看到之前修改的結果。
image
image
如果開啟 Inspector 可以直接點選畫面上想要調整的地方,左側功能列表就自動開啟相對應的設定
官方還有另一個 valencia 樣式可以選擇。
image

利用 Swipe Event 控制換頁

image
Carousel 效果是我認為在 Mobile 上面最難處理的部分,大部分的作法都是用一個長條 div 只顯示其中一部份,再用 css : Left 或是 Top 去控制顯示的部分。如果是 jQuery 早期的動畫效果 jquery.animate() 就算是 ipad2 跑起來也是超頓的,唯一的解法就是利用 –webkit-Transform 來做處理。
在 jQuery Mobile 上面預設在換頁的時候就會觸發 –webkit-Transform 來處理換頁的特效(在不支援的瀏覽器上面會直接換頁顯示),但是這邊的特效跟其他 Framework 呈現方式比較不一樣的是在觸控移動的時候 jQuery Mobile Swipe 的方式是當使用者手指移動超過一段距離之後,就觸發 Swipe 事件。其他的 framework 如果有將 touchstart ,touchmove ,touchend 分別做處理的話,就可以在手指移動的同時就做畫面移動的效果,然後在 touch end 手指離開計算距離決定要不要呈現下一頁,抑或是距離不足就停留在原本頁面。jQuery Mobile 目前看起來沒有太重視這一塊,感覺他們的主要目標還是在容易開發、廣泛支援上面。

Swipe Events

基本上 jQuery Mobile 就是讓你很容易套用,所以要取得滑動的事件只要註冊 swipe event 就可以了,jQuery Mobile 利用 javascript 的 touch event 如果長度超過一定距離就觸發 swipe event。

  • 預設在一秒之內移動水平距離超過 30px 而且垂直小於 20px ,就會觸發 swipe 事件。
  • $.event.special.swipe.horizontalDistanceThreshold = 130; 設定 horizontalDistanceThreshold 參數,將原本水平移動距離 30px 改為 130px 之後,他才會觸發 swipe 事件,這邊就是根據測試的習慣來修改,避免因為太容易換造成其實 user 並沒有想要換頁,但是事件被觸發。
  • durationThreshold Swipe 動作要在多少時間下被完成,如果超過這個時間就不觸發 Swipe 事件,預設 1000 (ms)。
  • verticalDistanceThreshold 垂直移動不能超過多少(px)才觸發 Swipe。
  • scrollSupressionThreshold 水平移動超過多少(px)就停止原本垂直滾動。

Example

一開始我先建立一個 js 物件

Site: {
PrePage: { Url: "" },
NextPage: { Url: "" }
}

全網站註冊 Swipe 事件,只註冊一次

$(document).ready(function () {
$.event.special.swipe.horizontalDistanceThreshold = 130;
$(document).bind("swipeleft", function (e) {
$.mobile.changePage(Site.NextPage.Url, { transition: 'slide' });
});
$(document).bind("swiperight", function (e) {
$.mobile.changePage(Site.PrePage.Url, { transition: 'slide', reverse: true });
});
});

在每一頁載入的時候去修改物件的值

$('div:jqmData(role="page")').live('pageinit', function () {
Pru.Site.PrePage.Url = '@(PreviousSibling.Url)';
Pru.Site.NextPage.Url = '@(NextSibling.Url)';
});

這邊我是用 mvc3 Razor 搭配 ASP.NET MVC SiteMap provider 來控制,強力推薦 ASP.NET MVC SiteMap provider 很好用喔。全網站事件我只有註冊一次,如果在每一次 pageinit (pageint 是 jQuery Mobile 事件,ajaxload 新頁面進來觸發) 重複註冊的話,就會變成一次 swipe 換了好多頁。也有考慮過利用 bind unbind 來控制,但是似乎不是很容易,目前感覺上還是用 js 物件操作會比較方便。


jQuery Mobile | jQuery Mobile

jQuery Mobile #2 切換頁面的特效處理

jQuery 只是 plugin 但是 jQuery Mobile 是 Framework ,他在一引用的時候就自動幫你擋掉所有 A.Link 的動作,就是要你照他的規矩來玩。也有內建很多效果在 A.Link 切換頁面的時候來展現。雖然說效果不比 Sencha Touch 或是其他的 Mobile js Framework ,但是 jQuery Mobile 強調的就是多支援,支援度絕對沒有別家比得上,不用再花很多時間去調整各家瀏覽器、各家 OS 還有各家 OS 上的不同瀏覽器,講到舌頭都打結就知道支援度是多麼可怕的東西。

data-transition

<a href="index.html" data-transition="pop">彈出效果</a>

jQuery Mobile 利用 data-transition 來設定不同的效果,有 slide , slideup ,slidedown ,pop ,fade ,flip 都只要設定上去就可以看到效果了。

<a href="/index.html" data-transition="reverse slide" class="上一頁">

Slide 預設是由右方滑入,如果想做左邊一頁的話就是要設定為 reverse slide 。非常貼心的一點是,當套用效果切換頁面的時候,這時候回上一頁也會使用反向的效果來顯示。

data-rel=”dialog”

<a href="/index.html" data-rel="dialog">

設定 dialog 的情況下,連結頁面會用彈出的來顯示,而且彈出不會記錄在瀏覽器的 history 裡面,上下頁就不會切到彈出這頁。

rel=”external”

<a href="/index.html" rel="external">

如果連結的位置是由 http://||https:// 開頭,jQuery Mobile 就不會幫你做任何處理,直接開啟新頁面。但是如果在內部連結可是也不想要 jQuery Mobile 做什麼事的話,就加上 rel=”external” ,假設說新頁面沒有依照 jQuery Mobile 的 Html 規範走的就必須加上,不然再 ajax loading 的時候就會因為判讀不正常出現 error。


http://jquerymobile.com/test/docs/pages/page-transitions.html