2010年12月16日

WP7 - Images Via the Web and Local

我們有時侯會需要加上一些美女照片,如果是用 Silverlight 建立的程式就可能得注意檔案的 Build Action,因為檔案位置在編譯後會有所不同。
我用了我最愛的 Anne Hathaway 照片來看一下,先建立一個 Windows Phone 7 Silverlight 的應用程式,在 ContentPanel 中,加入一些控制項(樣式設定請參考 範例程式)。
<Grid x:Name="ContentPanel" Grid.Row="1">
    <ScrollViewer>
        <StackPanel >
            <TextBlock x:Name="AH1TextBlock" Text="< Tab This >" Style="{StaticResource BaseTextBlockStyle}" />
            <TextBlock x:Name="AH2TextBlock" Text="< Tab this >" Style="{StaticResource OliveTextBlockStyle}" />
            <Image x:Name="AH1Image" Style="{StaticResource ImageStyle}" />
            <Image x:Name="AH2Image" Style="{StaticResource ImageStyle}" />
            <Image x:Name="ContentImage" Style="{StaticResource ImageStyle}" />
            <Image x:Name="ResourceImage" Style="{StaticResource ImageStyle}" />
        </StackPanel>
    </ScrollViewer>
</Grid>
接下來在專案中建立一個 Images 資料夾,在資料夾當中加入我最愛的2張照片,在屬性視窗中 (Properties) 中,將 AnneHathaway3.jpg 的 Build Action 改成 Content

改了 Build Action 會有什麼不一樣?
做一下編譯動作後,在 bin 底下的資料夾中會發現有一個 .xap 的檔案,用解壓縮程式解開來後你會發現剛剛我們建的 Images 的資料夾,裡頭有剛剛設成 Content 的那張圖片,另外一張圖片則會轉成 Binary 被包在元件 .dll 當中,如果用 Reflector 工具來看,你就可以看得到那張圖片在裡面。

那要用哪一個 Build Action 比較好?
 Charles Petzold eBook 上有提到說在 Creating High Performing Silverlight Applications for Windows Phone 這份文件中有提到,使用 Content 的方式可以降低元件 (Assebmly)的大小(因為不用把圖片嵌入在裡面了),這樣的話可以提昇應用程式開啟的速度。

接下來我們來讓圖片能顯示出來,做法很一般就在 XAML 的控制項上將相對路徑設在 Source 屬性上就能取得圖片資料。
<Image x:Name="ContentImage" Style="{StaticResource ImageStyle}"  Source="Images/AnneHathaway3.jpg" />
<Image x:Name="ResourceImage" Style="{StaticResource ImageStyle}" Source="Images/AnneHathaway4.jpg" />
如果需要利用程式動態的設定圖片,可參考下列程式碼,但是不可以指直接將一個字串的路徑設給 Image.Source。
ContentImage.Source = new BitmapImage(new Uri("Images/AnneHathaway3.jpg", UriKind.Relative));
如果 BuildActuin 是 Resource 還可利用如下的方式取得圖片資源。
StreamResourceInfo resourceInfo = Application.GetResourceStream(new Uri("/AssignImageSource;component/Images/AnneHathaway4.jpg", UriKind.Relative));
var bmp = new BitmapImage();
bmp.SetSource(resourceInfo.Stream);
ResourceImage.Source = bmp;
指定的路徑中 /AssignImageSource 是元件名稱,你可以在方案總管 (Solution Explorer) 點擊專案 >> 屬性 就可以看到元件名稱 (Assembly Name) 的設定輸入方塊, component是一個固定值,指的是目前的元件。

如果要指定網路上的圖片資源作法也是一樣。
AH1Image.Source = new BitmapImage(new Uri("http://images.huffingtonpost.com/2010-02-08-500x_anne.jpeg"));
如果檔案大一點需要顯示下載的進度,則可以利用 WebClient 來下載圖片,先在 XAML 增加一個 ProgressBar 用來顯示進度。
<ProgressBar x:Name="DownloadProgressBar" />
再建立一個 WebClient 並且指定讀取進度改變、讀取完畢的事件,並呼叫非同步讀取的事件 OpenReadAsync。
WebClient wc = new WebClient();
wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged);
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
wc.OpenReadAsync(new Uri("http://www.gaelick.com/wp-content/uploads/2010/02/AnneHathaway.jpg"));

//剛連線時會有連接時間的空隙,暫時先讓 ProgressBar 顯示
DownloadProgressBar.IsIndeterminate = true;
建立進度改變的方法,在事件中指定完成的比率給 ProgressBar,因 Framework 設計不允許不同執行緒存取 UI,所以改變時要用委派的方式讓原本建立 UI 的執行緒來變更。
void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    Dispatcher.BeginInvoke(() => DownloadProgressBar.Value = e.ProgressPercentage);
}
建立下載完畢的事件,在事件中將取回的資料流設給 BitmapImage。
void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    BitmapImage bitImage = new BitmapImage();
    bitImage.SetSource(e.Result);
    AH2Image.Source = bitImage;

    AH2Image.Visibility = Visibility.Visible;
    DownloadProgressBar.Visibility = Visibility.Collapsed;
    AH2TextBlock.Visibility = Visibility.Collapsed;
}
完畢。我看了一下覺得圖片的效果很好ㄚ,人漂亮,還可以用手機拉來拉去,我應該來加點功能自動上線抓她的圖片。

參考:
eBook: Programming Windows Phone 7, by Charles Petzold
How to: Create a Custom Indeterminate Progress Bar
Creating High Performing Silverlight Applications for Windows Phone Samples

沒有留言:

張貼留言