WP7 - Screenshot你的WP7 App

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>所介紹的方法,的確可以成功,往下將

介紹裡面使用到的關鍵技術:

 

WriteableBitmap

Silverlight 3的一個重要功能,提供一個可寫入與更新的BitmapSource(BitmapSource:提供使用點陣圖之屬性的來源物件)。

WriteableBitmap是以單格方式更新及呈現點陣圖。因此,透過單格方法更新的效果,可以常見到,例如:碎形影像、拼圖,

或是利用WriteableBitmap來針對UIElement產生視覺化的點陣圖快照

除了上述常見的作法之外,在MSDN裡也有看到相關擷取MediaElement中播放的視訊畫面格與網路攝影機擷取的快照,

可以分別參考:MediaElementCaptureSource.CaptureImageAsync

 

介紹WriteableBitmap類別,有二個重要的方法:

(a) Render(UIElement element, Transform transform)

      該方法用於指定要在點陣圖中呈現的項目,並且透過Transform將在繪製前,把項目依照指定的轉換方法先行轉換再寫入。

      呼叫完此方法之後,需再配合Invalidate方法,將結果呈現出來。注意:該方法不支援樹狀結構的UIElement。

(b) Invalidate:要求繪製或重繪整個點陣圖。

這二個方法的使用,可以參考<"Printing" in Silverlight 3 with WriteableBitmap>介紹的內容,使用起來會更有感覺。

 

MemoryStream

該類別主要作用在建立支援的存放區為記憶體的資料流。也就是說,MemoryStream會建立一個資料流,就像在記憶體區塊中先

切割出一塊可以被暫存的區域,不需要直接寫入檔案或與網路連接。MemoryStream會封裝它操作的資料實體,並且透過Byte[]

的方式加以初始化與操作儲存,這樣可以減少應用程式中所需的暫存緩衝區與檔案。

但要特別注意,MemoryStream使用「不帶正負號的位元組陣列所建立的記憶體資料流」,提供資料不可調整大小的資料流檢視,

並且只能被寫入,所以不可以在從中間增加或壓縮資料流,這是要注意的地方。

MemoryStream被使用的機會在WP7蠻多的,例如:轉換檔案、存取用戶暫時編輯的資料內容或做下載資料的緩衝,均會使用到該

類別,更詳細的介紹可以參考<MSDN -MemoryStream 類別>的介紹,另外,針對MemoeryStream與BufferedSteram二者使用上的

差異說明,更應該看一下這篇<[VB.NET][C#.NET] MemoryStream / BufferedStream 類別>。

 

MediaLibrary

主要提供程式去連接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) 執行結果

01 02

03 04

 

[注意事項]

(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库

 

 

 

Dotblogs Tags: