Windows 8開發初體驗 - C#版菜譜


作者:馬寧

示例代碼下載地址:http://files.cnblogs.com/aawolf/ContosoCookbook.zip

 

Windows 8的Release Preview版已經在2012年的兒童節正式發布了。雖然不如外界期望的那么成熟,Windows 8開始慢慢的學步了。作為開發者,我們面臨的挑戰要大於之前Windows的每一次升級。Windows 8對於開發者的挑戰,可能僅次於當年從DOS升到Windows,別忘了,無數在DOS下非常成功的軟件因為無法支持Windows而在一夜間煙消雲散。

這次Windows 8的挑戰主要來自於Metro UI,Metro UI是從Windows Phone衍生而來,適合移動設備多點觸摸輸入方式的UI Framework。而Metro UI的編程方式類似於Silverlight,結合了Windows Phone中的一些特點,但是Metro Style UI Framework卻和兩者都不相同。

Metro UI支持三種編程語言:.NET(C#,VB.NET)、C++和JavaScript。在這些編程語言的下面,是統一的WinRT Framework。

我們在這里就不多聊WinRT了,如果有興趣的,可以去看微軟的官方文檔:

http://msdn.microsoft.com/library/windows/apps/

最近討論C++和編程的比較多,反而是.NET編程有點冷清。所以,我在這里改寫了一個例子,供大家參考。這個例子原本是JavaScript版的Contoso Cookbook,在Windows 8訓練營時作為例子,應該很多人已JavaScript經拿到了。因為看不懂JavaScript,所以,我決定將其改寫成C#版的。其中的圖片的版權屬於微軟,如果有任何侵權問題,請通知我。我會進行更換。

好了,接下來,言歸正傳。

開發環境和創建工程

我們的開發環境是Windows 8 Release Preview和Visual Studio Ultimate 2012 RC版。如果沒有Visual Studio Ultimate 2012 RC版的朋友,也可以下載Visual Studio 2012 Express RC for Windows 8版,來開發Windows 8 Metro style應用。下載地址如下:

http://msdn.microsoft.com/en-US/windows/apps/br229516.aspx

3

進入到Visual Studio 2012后,發現整個界面都很Metro,還能夠切換黑背景和白背景,雖然圖標變化較大,但是操作模式基本沒有什么變化。所以,我們選擇Visual C#下的Windows Metro style項目,其中包括了Grid App(XAML)選項,我們將工程名改為ContosoCookbook,點擊OK。

初次創建Windows 8的應用,需要申請開發者許可。目前是免費的,不知道將來是否要收費。點擊“我同意”后,輸入Live ID的用戶名和密碼即可。

12

然后,我們就進入到了Visual Studio 2012的主界面。主界面變化不大,我們選擇直接運行應用程序,會進入到全屏的Metro UI界面,如下圖所示:

6

這個形式也是Metro style里使用較多的一種展現形式。好壞就不評價了,大家慢慢適應吧。

剛開始有一件郁悶的事情,不知道該怎么返回Visual Studio,在嘗試了N多方法后,發現按Win鍵+D,可以直接返回Visual Studio。

除了本機調試外,我們還能夠使用模擬器來進行調試,想必這就是Windows 8平板以后的調試方式了。在工具欄的運行按鈕后邊有一個下拉菜單,選擇“Simulator”,點擊運行,會打開一個模擬器,如圖:

7

這個模擬器中的狀態和本機是一致的,包括安裝的應用程序,但我們可以使用一些附加功能,來調試平板上特有的功能,比如:旋轉、定位、攝像頭等。最逗的是,這個模擬器沒有關閉按鈕,你唯一的辦法就是,在模擬器中找到關機界面,然后將其關閉。

修改應用名稱和圖標

接下來,我們修改應用程序的啟動界面和圖標。在Solution Explorer中,找到Assets目錄,其中需要修改的圖標文件有:Logo.png(150x150), SmallLogo.png(30x30)和StoreLogo.png(50x50),啟動界面是SplashScreen.png(620x300)。右鍵選中Assets,選擇添加已有項目“Add - Existing Item”,將准備好的圖片添加進來,覆蓋掉默認的圖標即可。

8

接下來,雙擊Package.appxmanifest文件,打開的是一個編輯器,可以設置與應用程序相關的屬性。修改其中的Display name和Description屬性,

9

除此之外,還有很多屬性可以設置,在這里就不一一介紹了。Packaging屬性頁里,有一個Guid的屬性,估計后邊還可能出現Windows Phone上幾個應用用一個Guid的情況了。

修改完了Logo和顯示名稱之后,再次運行,可以看到新的界面。

10

加載數據和圖片

接下來就到了重頭戲,要加載數據和圖片了。首先在Solution Explorer中創建兩個文件夾:data和images。data文件夾中添加Recipes.txt文件,其中包括了一組Json數據,包含了世界各地的美食介紹;images文件夾中,要包含所有的圖片文件,直接將圖片文件拖拽進去即可。

然后,打開DataModel目錄下的SampleDataSource.cs文件,在文件頂部添加引用:

using Windows.Storage;

using Windows.ApplicationModel;

using Windows.Data.Json;

然后在SampleDataSource類中增加一個新的方法來加載數據文件:

public static async System.Threading.Tasks.Task LoadFile()

{

StorageFolder folder = await Package.Current.InstalledLocation.GetFolderAsync("data"); ;

StorageFile file = await folder.GetFileAsync("Recipes.txt");

string text = await FileIO.ReadTextAsync(file);

}

Windows 8文件訪問方式發生了很大的變化,我花了一上午時間才搞定,最后的代碼卻很簡單。調用Package.Current.InstalledLocation屬性,就可以找到程序當前運行的路徑,該屬性返回一個StorageFolder對象。因為Recipes.txt文件放在data目錄中,所以還要調用GetFolderAsync方法,來獲取data子文件夾。GetFolderAsync方法是一個異步調用方法,所以我們需要使用一個新的關鍵字:await來等方法調用結束返回結果。await關鍵字只能在異步調用的方法中被調用,所以LoadFile方法聲明時,增加了async關鍵字,表示該方法是一個異步調用方法。關於await和async關鍵字,我們以后有機會詳細來說。

獲取了StorageFolder對象后,我們可以調用StorageFolder的GetFileAsync方法,來獲取文件的StorageFile對象。然后再使用FileIO的ReadTextAsync方法,將文件讀取到字符串中。

好了,與文件相關的操作就介紹到這里。接下來,我們看一下,如何分析JSON數據。

分析JSON數據

隨着JSON數據格式的流行,Metro style Framework也增加了對於JSON數據的支持。我們先來看看數據大概是什么樣的:

{"group":{"key":"Chinese","title":"Chinese","shortTitle":"Chinese","recipesCount":0,"description":"Lorem ipsum dolor sit amet","rank":"","backgroundImage":"images/Chinese/chinese_group_detail.png", "headerImage":"images/Chinese/chinese_group_header.png"},

"key":1000,

"title":"Chinese Salad",

"shortTitle" : "Chinese Salad", 

"preptime":30,

"favorite":false,

"rating": 3 , 

"directions":"Preheat the broiler.",

"backgroundImage":"images/Chinese/Chinese Salad.jpg",

"ingredients":["1 head Napa or bok choy cabbage","1/4 cup sugar","1/4 teaspoon salt","3 tablespoons white vinegar","3 green onions","1 (3-ounce) package ramen noodles with seasoning pack","1 (6-ounce) package slivered almonds","1 tablespoon sesame seeds","1/2 cup vegetable oil"]},

每項數據分屬不同的Group,而我們需要的Group數據有:ID、標題、子標題、描述和標題圖片;數據項的數據包括:Key、標題、子標題、背景圖片和菜譜。

所以,我們在LoadFile方法里增加下面的代碼,代碼略微有點長:

JsonArray parsedResponse = JsonArray.Parse(text);

if (parsedResponse.Count > 1)

{

string groupid = "";

SampleDataGroup group1 = null;

foreach (JsonValue value in parsedResponse)

{

var obj = value.GetObject();

var group = obj["group"].GetObject();

string id = group["key"].GetString();

if (id != groupid)

{

groupid = id;

group1 = new SampleDataGroup(

group["key"].GetString(),

group["title"].GetString(),

group["shortTitle"].GetString(),

group["headerImage"].GetString(),

group["description"].GetString()

);

_sampleDataSource.AllGroups.Add(group1);

}

group1.Items.Add(new SampleDataItem(

obj["key"].GetNumber().ToString(),

obj["title"].GetString(),

obj["shortTitle"].GetString(),

obj["backgroundImage"].GetString(),

obj["directions"].GetString(),

ITEM_CONTENT, 

group1)

);

}

我們首先調用JsonArray.Parse方法來解析剛剛獲取到的Json數據,返回一個JsonArray的數組對象。接下來,通過foreach來遍歷JsonArray中的數據項,獲取到的對象是JsonValue對象。因為獲取到的數據是對象,所以,我們使用JsonValue的GetObject方法來獲取一個對象,雖然代碼里用的是var,實際的數據類型是JsonObject。然后用obj["group"].GetObject()方法來獲取到Group信息的JsonObject,中括號中必須填寫Key的名稱。然后取出Group的Id值,該值是一個字符串,所以調用GetString方法來獲取Id值。如果讀取出的Id值與之前不同,則創建新的SampleDataGroup對象,對象中所需的數據則通過Group對象的GetString方法來獲取;然后,將其添加到SampleDataSource 的AllGroups屬性中。

接下來,就是創建SampleDataItem對象,這次要從obj對象里取到所需信息,如果JSON數據中是數字,則調用GetNumber方法。最后將創建好的SampleDataItem對象添加到SampleDataGroup的Items列表中。

到這里,我們關於JSON的代碼就寫完了。接下來,擺在我們面前的還有一個問題。

解決異步調用的問題

接下來的代碼似乎順理成章了。我們在SampleDataSource的構造函數中,將里邊的示例代碼全部刪除,然后添加LoadFile方法,如下圖:

public SampleDataSource()

{

LoadFile();

}

運行之后的效果卻是如下:

clip_image016

Group的數據被加載了,但是其中的數據項未被加載。而我們點擊Chinese項目時,則能夠看到中國美食的情況,再退回主界面,所有的數據就能夠顯示出來了。這是典型的異步調用錯誤。原因也很簡單,是因為SampleDataSource的構造函數是同步調用,不會等LoadFile結束后再返回,所以,當界面上顯示數據時,顯示的只是未加載完成的數據列表。

頭疼的問題啊,我試了幾種方案,最后采取的這種方案未必是最好的,但確實能夠解決這個問題。也許大家有更好的解決方法,留一個懸疑吧。接下來,我說我的辦法。

由於構造函數無法變成async調用方法,所以只能用另外的一個顯式初始化函數來替代構造函數的作用,在數據顯示之前,顯式地調用該初始化函數。所以,我就直接將LoadFile作為這個初始化函數,為LoadFile增加public,static和async關鍵字。

然后,我們打開GroupItemsPage.xaml.cs文件,找到LoadState方法。該方法用來設置當前視圖的數據源,所以,我們在LoadState方法的頂部顯式調用SampleDataSource.LoadFile方法。當然,我們會得到一個編譯錯誤,因為LoadState方法也是一個同步調用函數,簡單地為LoadState方法增加一個async的關鍵字就可以解決這個問題。代碼如下:

protected async override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)

{

await SampleDataSource.LoadFile();

var sampleDataGroups = SampleDataSource.GetGroups((String)navigationParameter);

this.DefaultViewModel["Groups"] = sampleDataGroups;

}

好了,當我們終於做完這一切之后,我們就可以看到完整的用戶界面了:

clip_image018

clip_image020

clip_image022

寫在最后

看起來很簡單的代碼,卻整整寫了一天,期間試過的方法也很多,一言難盡。目前Windows 8的開發資料也比較少,我在開發過程中,找到了下面的例子“Windows 8 Release Preview Metro style app samples - C#, VB.NET, C++, JavaScript”,在其中找到很多有用的代碼:

http://code.msdn.microsoft.com/windowsapps/Windows-8-Modern-Style-App-Samples

好了,這次的例子包括了數據綁定、JSON數據分析和文件讀取,希望后邊還有時間能夠寫更多的例子出來。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM