Windows Phone 7 – Screenshot你的WP7 App
還記得之前WP7剛推出,馬上就有人推出了「WP7 Screenshot Tool」的功能嗎?
在WP7還沒有提供允許像iPhone直接按下home+power就可以拍照的功能之前,對於要screen shot手機上的畫面,
真的是非常的不容易(雖然每次在MS展示WP7功能的的場子都有看到他們把手機畫面直接show出來)。因此,有沒有
什麼方法至少可以讓我們把自己開發的App做到Screen shot的功能呢?
答案是有的!因為Silverlight本身就有提供這樣的功能存在,因此,讓我們往下看一下究竟要怎麼做吧。
根據<Taking a screenshot from within a Silverlight #WP7 application>所介紹的方法,的確可以成功,往下將
介紹裡面使用到的關鍵技術:
Silverlight 3的一個重要功能,提供一個可寫入與更新的BitmapSource(BitmapSource:提供使用點陣圖之屬性的來源物件)。
WriteableBitmap是以單格方式更新及呈現點陣圖。因此,透過單格方法更新的效果,可以常見到,例如:碎形影像、拼圖,
或是利用WriteableBitmap來針對UIElement產生視覺化的點陣圖快照。
除了上述常見的作法之外,在MSDN裡也有看到相關擷取MediaElement中播放的視訊畫面格與網路攝影機擷取的快照,
可以分別參考:MediaElement與CaptureSource.CaptureImageAsync。
介紹WriteableBitmap類別,有二個重要的方法:
(a) Render(UIElement element, Transform transform):
該方法用於指定要在點陣圖中呈現的項目,並且透過Transform將在繪製前,把項目依照指定的轉換方法先行轉換再寫入。
呼叫完此方法之後,需再配合Invalidate方法,將結果呈現出來。注意:該方法不支援樹狀結構的UIElement。
(b) Invalidate:要求繪製或重繪整個點陣圖。
這二個方法的使用,可以參考<"Printing" in Silverlight 3 with WriteableBitmap>介紹的內容,使用起來會更有感覺。
該類別主要作用在建立支援的存放區為記憶體的資料流。也就是說,MemoryStream會建立一個資料流,就像在記憶體區塊中先
切割出一塊可以被暫存的區域,不需要直接寫入檔案或與網路連接。MemoryStream會封裝它操作的資料實體,並且透過Byte[]
的方式加以初始化與操作儲存,這樣可以減少應用程式中所需的暫存緩衝區與檔案。
但要特別注意,MemoryStream使用「不帶正負號的位元組陣列所建立的記憶體資料流」,提供資料不可調整大小的資料流檢視,
並且只能被寫入,所以不可以在從中間增加或壓縮資料流,這是要注意的地方。
MemoryStream被使用的機會在WP7蠻多的,例如:轉換檔案、存取用戶暫時編輯的資料內容或做下載資料的緩衝,均會使用到該
類別,更詳細的介紹可以參考<MSDN -MemoryStream 類別>的介紹,另外,針對MemoeryStream與BufferedSteram二者使用上的
差異說明,更應該看一下這篇<[VB.NET][C#.NET] MemoryStream / BufferedStream 類別>。
主要提供程式去連接Device中的音樂、播放清單、影片與圖片的多媒體庫,進行擷取與編輯。擷取MediaLibrary中的資料,
它採用media collections集合回傳的方式,其回傳的內容包括: Albums, Artists, Genres, Pictures, Playlists, and Songs,
因此,在操作上通常會配合列舉與index來使用。這類別在XNA上很常使用,包括:取得音效、操作圖片或遊戲快照等。
但它的使用非常容易,只需實例化該類別,再依照要取得的媒體對象,就可以透過集合的操作得到結果。
以上是簡介了要做快照時會需要用到的元件,接下來是重點:
〉實作範例:
(1) 建立一個獨立的方法,透過指定的FrameworkElement元件與指定檔案名稱來進行Screen shot;
1: //建立一個獨立方法
2: public void SaveToMediaLibrary(FrameworkElement pElement, String tTitle)
3: {
4: try
5: {
6: //宣告WriteableBitmap並指定要取得的Element對象
7: var tBitmap = new WriteableBitmap(pElement, null);
8:
9: //使用MemoryStream來暫存screen shot的byte[]
10: var tMStream = new MemoryStream();
11:
12: //將Element轉成圖片並寫入MemoryStream中
13: tBitmap.SaveJpeg(tMStream, (int)pElement.ActualWidth, (int)pElement.ActualHeight, 0, 100);
14: tMStream.Seek(0, SeekOrigin.Begin);
15:
16: //取得MediaLibrary(需依賴Microsoft.XNA.Framework.Media)
17: var tLibrary = new MediaLibrary();
18: var tFilePath = String.Format(tTitle + ".jpg");
19: //儲存檔案
20: tLibrary.SavePicture(tFilePath, tMStream);
21:
22: MessageBox.Show("儲存Screenshot圖檔至媒體庫中!");
23: }
24: catch (Exception ex)
25: {
26: MessageBox.Show(ex.Message);
27: }
28: }
(2) 增加使用PhotoChooserTask的Task元件,將Screen shot產生的圖片讀回來,並使用Popup元件show出來;
1: //放置一個Popup元件來顯示擷取到的畫面結果
2: private Popup gPopupControl = null;
3:
4: private void OpenPhoto(object sender, EventArgs e)
5: {
6: if (gPopupControl != null)
7: gPopupControl.IsOpen = false;
8: //使用PhotoChooserTask取得screen shot結果
9: PhotoChooserTask tPhotoTask = new PhotoChooserTask();
10: tPhotoTask.Completed += new EventHandler<PhotoResult>(tPhotoTask_Completed);
11: tPhotoTask.Show();
12: }
13:
14: void tPhotoTask_Completed(object sender, PhotoResult e)
15: {
16: if (e.TaskResult == TaskResult.OK)
17: {
18: var tBitmap = new BitmapImage();
19: //將取得結果放入BitmapImage中
20: tBitmap.SetSource(e.ChosenPhoto);
21: Image tImage = new Image();
22:
23: tImage.Source = tBitmap;
24:
25: if (gPopupControl == null)
26: {
27: gPopupControl = new Popup();
28: }
29: //設定顯示的popup元件
30: gPopupControl.Child = tImage;
31: gPopupControl.IsOpen = true;
32: tBitmap = null;
33: }
34: }
(3) 執行結果
[注意事項]
(a) 由於會使MediaLibrary的功能去存取Pictures裡的資料,因此,當開著zune做測試是會失敗的;
(b) 使用Emulator進行測試時,需要另外搭配PhotoChooserTask來使用才可以取得拍下來的圖;
(c) 需要using Microsoft.XNA.Framework.dll,因會用到Microsoft.XNA.Framework.Media中的類別;
(d) 檢查WPAppManifest.xml中是否已宣告「<Capability Name="ID_CAP_MEDIALIB" />」,告知此程式會用到MediaLibrary;
(f) Taking a screenshot from within a Silverlight #WP7 application提到FrameworkElement元件大部分支援,但Webbrowser沒有;
======
以上是分享在網路上看到做一個Screen shot的功能,用來擷取自己App上的畫面。(因為發現很多App都有提供!)
由於現在WP7還沒有提供像Android有長駐的Service,沒有辦法直接把這個方法放在背景來擷取其他App操作的畫面,
所以先透過Silverlight本身提供的功能來完成暫時的需求。但我相信這功能應該會很快有人做出來吧。
到時,就可以很方便的擷取自己畫面裡的功能,當然最好也可以直接把畫面投影至pc上是最好啦。
References:
‧Loading, iterating, and saving photos with WP7
‧"Printing" in Silverlight 3 with WriteableBitmap (進階)
‧SL3: Programmatically Take and Save Screenshot?
‧Taking a screenshot from within a Silverlight #WP7 application (基本)
‧Convert, Encode And Decode Silverlight WriteableBitmap Data
‧Silverlight WriteableBitmap使用
‧Silverlight 3 – The Bitmap API / WriteableBitmap
‧[VB.NET][C#.NET] MemoryStream / BufferedStream 類別
‧范围windowsphone7的mediaLibrary库