[Silverlight]學習筆記-使用 Deep Zoom 功能並顯示Sub-Images的Metadata(2)
在進入本篇主題之前,先來看一下Hard Rock Cafe Memorabilia網站(底下簡稱Hard Rock網站)顯示Sub-Images的Metadata方法。
剛開始進入Hard Rock網站,頁面右方的MultiScaleImage顯示如下:
當我們使用滑鼠滾輪將圖片放大到某種程度,MultiScaleImage的右側就會自動出現該圖片的資訊。
這裡顯示的資訊有二種方式,其一當滑鼠的操作為MouseDown、MouseUp時,則點選的圖片會置中並放大,顯示的訊息即為點選圖片的資訊。而其二當滑鼠的操作為MouseMove、MouseWheel時,資訊顯示的是位於MultiScaleImage中心點那張圖片的資訊。
接著來看一下圖片資訊右上角個有個收合符號,點選它就可以將訊資的內容收合隱藏,隱藏之後則右上角的符號會改變,並出現More info字樣,若再點選它,則隱藏的資訊又會再展開。
在上一篇的[Silverlight]學習筆記-使用 Deep Zoom 功能並顯示Sub-Images的Metadata(1)中,已簡單介紹在Deep Zoom底下如何顯示SubImages的Metadata資訊,此例再使用Deep Zoom功能來做一個類似上述Hard Rock網站一樣的自動顯示圖片資訊的效果。
首先,使用Deep Zoom Composer(DZC)依上一篇DZC Project的建立方式,在第三步驟Export中,Export options裡有五種Templates選項,這五種Templates在DZC User Guide的說明如下:
此例因要另外使用Blend 4進行後續的設定,所以就選擇 Expression Blend 4 + Source來練習。
使用檔案總管看一下Export出來的內容如下,所有DeepZoom的功能都有各自的class。
接著使用 Blend 4 開啟剛建立的 DeepZoomProject.sln 方案,並調整其中Page.xaml頁面配置如下:
這裡新增一個Border容器(InfoBorder),其內再放一個Canvas容器(InfoCanvas),InfoCanvas容器內放入三個TextBlock控制項以便顯示影片資訊。
XAML的內容如下:
<Canvas x:Name="LayoutRoot" Background="Black" Height="480" Width="800" >
<MultiScaleImage x:Name="msi" Source="/GeneratedImages/dzc_output.xml" Height="480" Width="800">
</MultiScaleImage>
<Canvas Height="37" HorizontalAlignment="Right" x:Name="buttonCanvas" Width="348" Opacity="0" Background="{x:Null}" Canvas.Left="444" Canvas.Top="435">
<Button Height="30" x:Name="zoomIn" Width="42" Canvas.Left="197" Canvas.Top="4" Template="{StaticResource zoomInTemplate}" Content="Button" />
<Button Height="30" x:Name="zoomOut" Width="42" Template="{StaticResource zoomOutTemplate}" Content="Button" Canvas.Left="227" Canvas.Top="4" />
<Button Height="30" x:Name="goHome" Width="42" Template="{StaticResource homeTemplate}" Content="Button" Canvas.Left="257" Canvas.Top="4" />
<Button Height="30" x:Name="fullScreen" Width="42" Template="{StaticResource fullScreenTemplate}" Content="Button" Canvas.Left="287" Canvas.Top="4" />
</Canvas>
<Border x:Name="InfoBorder" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Right" Height="480" VerticalAlignment="Top" Width="160" RenderTransformOrigin="1,0" Background="#7F000000" Canvas.Left="640">
<Canvas x:Name="InfoCanvas" Margin="0,0,-2,-2">
<TextBlock x:Name="tbECapital" Canvas.Left="8" TextWrapping="Wrap" Text="TextBlock" Canvas.Top="38" Width="144" Height="16" Foreground="White"/>
<TextBlock x:Name="tbCCapital" Canvas.Left="8" TextWrapping="Wrap" Text="TextBlock" Canvas.Top="71" Width="144" Height="16" Foreground="White"/>
<TextBlock x:Name="tbDesciption" Canvas.Left="8" TextWrapping="Wrap" Text="TextBlock" Canvas.Top="101" Width="144" Height="371" Foreground="White"/>
</Canvas>
</Border>
</Canvas>
接下來我另外準備了一個類似Hard Rock網站上右上角收合圖片資訊的自訂控制項ArrowCheckBox,該控制項的狀態如下:
左邊為控制項UnChecked時的狀態,箭頭向左,左方有More info字樣。中間為MouseOver狀態,箭頭的Alpha值提高。右邊為Checked狀態,箭頭向右,原先的More info字樣消失。
Blend自訂控制項可參考[Silverlight]天啊!我把CheckBox變成圖釘了!!一文,在此加碼簡述一下此ArrowCheckBox控制項的作法如下:
1.使用Blend在DeepZoom Project底下新增UserControl項目,名稱改為ArrowCheckBox.xaml。
2.在ArrowCheckBox的LayoutRoot中任意拉一Ellipse楕圓形,並使其Width=Height=100變成圓形。另外拉三個Path線條,再將三個線條組成左箭號的形狀放在Ellipse內,並將所有物件其筆刷的Stroke全設成相同的顏色,Ellipse的填充顏色Fill順便也改一下,調整完頁面如下:
3.將Ellipse及三個Path全部選取,並於其上按滑鼠右鍵,選取【路徑】/【製作複合路徑】,將四個物件組成一個Path。
4.接著調整組合成功的Path的Width及Height屬性縮小為20,而UserControl及LayoutRoot的Width=80,Height=20,並調整所有物件的Margin及HorizontalAlignment,調整完頁面如下:
整個XAML的內容如下:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="DeepZoomProject.ArrowCheckBox"
d:DesignWidth="640" d:DesignHeight="480" Width="80" Height="20">
<Canvas x:Name="LayoutRoot" Width="80" Height="20">
<Path Data="M19.5,10 C19.5,15.246705 15.246705,19.5 10,19.5 C4.7532949,19.5 0.5,15.246705 0.5,10 C0.5,4.7532949 4.7532949,0.5 10,0.5 C15.246705,0.5 19.5,4.7532949 19.5,10 z M3.6319995,10.086001 L16.873999,10.086001 M3.9860034,10.086 L8.5920029,5.480001 M3.9860034,10.086001 L8.5920029,14.692001" Fill="Black" Height="20" Canvas.Left="60" Stretch="Fill" Stroke="#FFC87800" UseLayoutRounding="False" Width="20"/>
</Canvas>
</UserControl>
5.接著在組合成功的Path上按滑鼠右鍵,選取【變成控制項】,於對話視窗的控制項類型中,選取CheckBox,名稱改為ArrowCheckBoxStyle,然後按【確定】鈕。
6.變成控制項後,調整其中ContentPresenter文字至左方,並修改Path箭號圖的Alpha值為50%。
7.接著開始為控製項設定狀態。
切換到狀態頁籤,然後選擇MouseOver,再選擇Path物件,設定筆刷的Alpha由50%改為80%。
再選擇CheckStates底下的Checked,再選擇ContentPresenter物件,將其Visible屬性設為Collapsed。
最後再選擇Path物件,設定其RenderTransform為翻轉X軸,讓原先的向左的箭號變成向右的箭號。
狀態設定完成後,執行建置專案,讓控制項生效。
自訂控制項介紹到此。
再回到Page.xaml看其它的設定。
1.拉一個CheckBox控制項到頁面上,將該控制項名稱改為chkShowInfo,Foreground設為#FFFFFFFF(白色),Content改為More info,再設定其Style為ArrowCheckBoxStyle,這個CheckBox控制項的外型就會變成之前完成的ArrowCheckBox箭號樣式。最後再將該控制項移到頁面的右上角處。
2.選取InfoBorder容器,先設定其轉換縮放中的X軸縮放比例為0,將其隱藏起來。
3.切換到狀態頁籤,點選新增狀態群組以新增一個狀態群組,並將其改名為 InfoStateGroup。
3.在InfoStateGroup狀態群組底下,新增二個狀態,分別命名為 ShowInfoBorder及HideInfoBorder。
4.先選擇ShowInfoBorder狀態,再選擇InfoBorder容器,然後設定其轉換縮放中的X軸縮放比例為1,讓其顯示。
5.切換到資產頁籤,拉二個GoToStateAction行為到ArrowCheckBox控制項底下,分別命名為ShowStateAction及HideStateAction。
這二個行為的設定分別如下:
到此先存檔,接來的工作交給Visual Studio 2010。
接著使用 VS 2010 開啟 DeepZoomProject.sln 方案:
1.先把上個練習範例中電影資訊檔MoviesContent.XML加進方案中。
2.為了達成前述Hard Rock網站顯示圖片資訊的功能,我們必須要知道MultiScaleImage的三項資訊才能進行,這三項必要的資訊為 : 目前的縮放比、點擊了哪張圖片以及滑鼠是否為拖曳狀態。
開啟DeepZoomInitializer.cs,替MultiScaleImage追加三個DependencyProperty以記錄上述三項資訊,分別為ZoomFactorProperty、SelectedIndexProperty、DuringDragsProperty。
public static readonly DependencyProperty ZoomFactorProperty = DependencyProperty.Register("ZoomFactor",typeof(double),typeof(MultiScaleImage), null);
public static readonly DependencyProperty SelectedIndexProperty = DependencyProperty.Register("SelectedIndex",typeof(int),typeof(MultiScaleImage), null);
public static readonly DependencyProperty DuringDragsProperty = DependencyProperty.Register("DuringDrags", typeof(bool), typeof(MultiScaleImage), null);
並且在Zoom()裡賦與ZoomFactor值給ZoomFactorProperty
private void Zoom(double newzoom, Point p)
{
// Do something
msi.SetValue(ZoomFactorProperty, this.ZoomFactor);
}
另外在這裡加入將點擊圖片置中的功能,並且賦與值給SelectedIndexProperty及DuringDragsProperty。
msi.MouseLeftButtonUp += delegate(object sender, MouseButtonEventArgs e)
{
if (!duringDrag)
{
// Do something
selectedIndex = SubImageHit(this.msi.ElementToLogicalPoint(e.GetPosition(this.msi)));
if (selectedIndex >= 0)
this.DisplaySubImageCentered(selectedIndex); //點擊的圖片置中
Zoom(newzoom, msi.ElementToLogicalPoint(this.lastMousePos));
msi.SetValue(SelectedIndexProperty, selectedIndex);
}
msi.SetValue(DuringDragsProperty, duringDrag);
};
3.切換到 Page.xaml.cs ,先加這二個namespace (必須先將 System.Xml.Linq.dll 加入參考)
using System.Xml.Linq;
using System.Linq;
然後再宣告三個Property,透過這三個Property去取得MultiScaleImage在DeepZoomInitializer所追加的那三個DependencyProperty。
public double ZoomFactor
{
get { return (double)msi.GetValue(DeepZoomInitializer.ZoomFactorProperty); }
}
public int SelectedIndex
{
get { return (int)msi.GetValue(DeepZoomInitializer.SelectedIndexProperty); }
}
public bool DuringDrags
{
get { return (bool)msi.GetValue(DeepZoomInitializer.DuringDragsProperty); }
}
另外追加MultiScaleImage三個EventHandler
public Page()
{
InitializeComponent();
this.msi.ImageOpenSucceeded += new RoutedEventHandler(msi_ImageOpenSucceeded);
this.msi.MouseLeftButtonUp += new MouseButtonEventHandler(msi_MouseLeftButtonUp);
this.msi.MouseWheel += new MouseWheelEventHandler(msi_MouseWheel);
}
其中msi_ImageOpenSucceeded的內容如上個範例,主要是載入影片的資訊,在此不再贅述。
而msi_MouseLeftButtonUp及msi_MouseWheel的內容如下:
private void msi_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (!this.DuringDrags)
{
imageIndex = this.SelectedIndex; //取得點擊的圖片索引
if (imageIndex < 0)
imageIndex = SubImageHit(this.msi.ElementToLogicalPoint(new Point(this.msi.ActualWidth / 2, this.msi.ActualHeight / 2))); //取得中心點所在的圖片索引
}
else
{
imageIndex = SubImageHit(this.msi.ElementToLogicalPoint(new Point(this.msi.ActualWidth / 2, this.msi.ActualHeight / 2))); //取得中心點所在的圖片索引
}
this.MappingInfo(imageIndex); //取得對應的影片訊息
}
private void msi_MouseWheel(object sender, MouseWheelEventArgs e)
{
imageIndex = SubImageHit(this.msi.ElementToLogicalPoint(new Point(this.msi.ActualWidth / 2, this.msi.ActualHeight / 2))); //取得中心點所在的圖片索引
this.MappingInfo(imageIndex); //取得對應的影片訊息
}
在msi_MouseLeftButtonUp裡先判斷是否有點擊圖片,若有則取得點擊圖片索引,若無(ex.點擊在圖片外的範圍)則取得MultiScaleImage中心點所在的那張圖片索引,MultiScaleImage的中心點可用new Point(this.msi.ActualWidth / 2, this.msi.ActualHeight / 2)取得。 取得圖片索引值之後,再透過MappingInfo()去取得該圖片的相關影片訊息及判斷目前的ZoomFactor是否需要顯示訊息。
而在msi_MouseWheel裡,則直接則取得MultiScaleImage中心點所在的那張圖片索引,再透過MappingInfo()去取得該圖片的相關影片訊息及判斷目前的ZoomFactor是否需要顯示訊息。
另外,MappingInfo()取得影片訊息的內容如下,其中ZoomFactor>=2.8才會顯示影片資訊,這個條件可視情況自行調整。
//取得對應的影片訊息
private void MappingInfo(int index)
{
if (index >= 0)
{
//由ImageInfo取得該張圖片的影片資訊
SubImageInfo subImageInfo = ImageInfo.FirstOrDefault(s => s.Index == index);
tbECapital.Text = subImageInfo.E_Caption; //英文片名
tbCCapital.Text = subImageInfo.C_Caption; //中文片名
tbDesciption.Text = subImageInfo.Description; //影片說明
}
else
{
tbECapital.Text = string.Empty;
tbCCapital.Text = string.Empty;
tbDesciption.Text = string.Empty;
}
// 判斷是否要秀影片訊息
if (index < 0)
{
this.ShowInfo(false);
return;
}
if (this.ZoomFactor >= 2.8) //當縮放值>2.8則顯示資訊,此值可視情況自行調整
this.ShowInfo(true);
else
this.ShowInfo(false);
}
private void ShowInfo(bool show)
{
if (show)
{
chkShowInfo.Visibility = Visibility.Visible;
InfoBorder.Visibility = Visibility.Visible;
}
else
{
InfoBorder.Visibility = Visibility.Collapsed;
chkShowInfo.Visibility = Visibility.Collapsed;
}
}
最後來看一下執行結果:
完整的範例程式碼可於此下載 MoviesList2.zip
參考資料:
[Silverlight]天啊!我把CheckBox變成圖釘了!!
電影圖片及資料來源: