阅读本文之前请确认你已经安装了如下软件
- Visual Studio 2008 (Express) SP1
- Silverlight 3 Tools For Visual Studio
- Microsoft Expression Blend 3 MIX 09 Preview
本文的ShowCase是一个3D的图片走马灯程序
图片的来源是来自Flickr图片共享网站
大家可以通过搜索关键字来获取得到8张图片
在线Demo
效果图
关键代码:
本文的图片来自Flickr,为了获取得到图片
我们需要了解下Flickr Service API
在这里采用flickr.photos.search这个接口来搜索并获取图片
于是我们就有了我们搜索的图片的Rest Url了
private string SearchFormattedUrl = "http://api.flickr.com/services/rest/?method=flickr.photos.search
&api_key={0}&text={1}&safe_search=1&sort=relevance&per_page={2}&page={3}";
其中{0}用来输入Flickr的APIKey(只有提供这个才能访问Flickr的API)
{1}用来提供我们搜索的关键字
{2}用来提供每页返回多少个结果
{3}用来提供搜索的是第几页
采用如下方法来返回结果
1: public FlickrGallery(string apiKey)
2: {
3: APIKey = apiKey;
4: }
5:
6: public void SearchPhotos(string query, uint per_page, uint page, Action<string, List<ImageInfo>> photosCallback)
7: {
8: Uri searchUri = new Uri(String.Format(SearchFormattedUrl, APIKey, query, per_page.ToString(), page.ToString()));
9: WebClient imageClient = new WebClient();
10: imageClient.DownloadStringAsync(searchUri);
11: imageClient.DownloadStringCompleted += delegate(object sender, DownloadStringCompletedEventArgs e)
12: {
13: if ((e.Cancelled == false) && (e.Error == null))
14: {
15: try
16: {
17: string xmlStr = e.Result;
18: if (!String.IsNullOrEmpty(xmlStr))
19: {
20: XDocument doc = XDocument.Parse(xmlStr);
21: var imgs = from flickrImg in doc.Descendants("photo")
22: select new ImageInfo()
23: {
24: Title = (string)flickrImg.Attribute("title"),
25: ImageUrl = String.Format(ImageUrlFormat, flickrImg.Attribute("farm").Value, flickrImg.Attribute("server").Value, flickrImg.Attribute("id").Value, flickrImg.Attribute("secret").Value)
26: };
27: List<ImageInfo> images = imgs.ToList();
28:
29: photosCallback(query, images);
30: return;
31: }
32: }
33:
34: catch
35: {
36: photosCallback(query, null);
37: }
38: }
39:
40: else
41: {
42: photosCallback(query, null);
43: }
44: };
45: }
这里采用了LINQ语句来Parse获取得到的XML文件
接下来就是在我们的主XMAL文件中获取图片信息了
1: private void SearchImagesInfo()
2: {
3: string query = SearchBox.Text.Trim();
4: if (query != String.Empty)
5: {
6: this.SearchingProgress.IsActive = true;
7: this.ImagePanel.Children.Clear();
8: photoGalley.SearchPhotos(query, 8, 1, OnPhotoGalleryPhotosSearched);
9: }
10: }
11:
12: private void OnPhotoGalleryPhotosSearched(string query, List<ImageInfo> photos)
13: {
14: this.SearchingProgress.IsActive = false;
15: foreach (ImageInfo photo in photos)
16: {
17: BitmapImage bitmap = new BitmapImage(new Uri(photo.ImageUrl, UriKind.Absolute));
18:
19: int index=photos.IndexOf(photo);
20:
21: AddTheImage(bitmap, index);
22: }
23: }
其中SearchingProgress是一个效果控件
用来指示当前是否还在下载图片信息,其效果图如下
通过其属性IsActive来控制其是否可见
接下来就是将获取得到的图片添加到将用来放置图片的面板上了
1: private void AddTheImage(ImageSource source,int index)
2: {
3: Image image = new Image()
4: {
5: Source = source
6: };
7:
8: image.Width = 200;
9: image.Height = 150;
10: image.HorizontalAlignment = HorizontalAlignment.Left;
11:
12: DropShadowEffect dsEff = new DropShadowEffect();
13: dsEff.ShadowDepth = 10;
14: dsEff.BlurRadius = 10;
15: dsEff.Color = Color.FromArgb(120, 60, 60, 60);
16:
17: image.Effect = dsEff;
18:
19: PlaneProjection projection = new PlaneProjection();
20: projection.LocalOffsetY = -index * 10;
21: projection.LocalOffsetX = index * 70;
22: projection.LocalOffsetZ = -index * 10;
23: image.Projection = projection;
24: image.MouseLeftButtonDown += new MouseButtonEventHandler(image_MouseLeftButtonDown);
25: this.ImagePanel.Children.Add(image);
26: }
在这里我对图片加了DropShadowEffect特效
大家也可以通过自定义一个投影图片来展示效果
此外这里使用了PlaneProjection来展示3D效果
我根据当前图片的编号来3D安排这些图片的位置(行19~23)
下面就是处理图片被点击的状况下图片的移动效果了
1: void image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
2: {
3: Image clickedImage = sender as Image;
4:
5: List<UIElement> elems = this.ImagePanel.Children.ToList();
6: int clickedIndex = elems.IndexOf(clickedImage);
7:
8: if (clickedIndex != 0)
9: {
10: if (isAnimateCompleted)
11: MoveImages();
12: }
13: }
14:
15: private void MoveImages()
16: {
17: if (this.ImagePanel.Children.Count > 0)
18: {
19: isAnimateCompleted = false;
20: Image firstImage = this.ImagePanel.Children[0] as Image;
21: this.ImagePanel.Children.RemoveAt(0);
22: AddTheImage(firstImage.Source, 8);
23: Storyboard sb = new Storyboard();
24: for (int index = 0; index < this.ImagePanel.Children.Count; index++)
25: {
26: Image img = this.ImagePanel.Children[index] as Image;
27:
28: double seconds = 1.0 + index * 0.03;
29:
30: sb = AnimateStory(img, seconds, -70, 10, 10);
31: sb.Begin();
32: }
33:
34: sb.Completed += (sender, e) =>
35: {
36: isAnimateCompleted = true;
37: };
38: }
39: }
如果点击的是第一张图片,将不会产生移动
而移动的效果我们采用自定义一个移动的Storyboard来处理
1: private Storyboard AnimateStory(UIElement elem, double seconds, double deltaX, double deltaY, double deltaZ)
2: {
3: Storyboard sb = new Storyboard();
4:
5: DoubleAnimation dAnimX = new DoubleAnimation();
6: dAnimX.By = deltaX;
7: dAnimX.Duration = new Duration(TimeSpan.FromSeconds(seconds));
8: Storyboard.SetTargetProperty(dAnimX, new PropertyPath("(UIElement.Projection).(PlaneProjection.LocalOffsetX)"));
9: Storyboard.SetTarget(dAnimX, elem);
10:
11: DoubleAnimation dAnimY = new DoubleAnimation();
12: dAnimY.By = deltaY;
13: dAnimY.Duration = new Duration(TimeSpan.FromSeconds(1));
14: Storyboard.SetTargetProperty(dAnimY, new PropertyPath("(UIElement.Projection).(PlaneProjection.LocalOffsetY)"));
15: Storyboard.SetTarget(dAnimY, elem);
16:
17: DoubleAnimation dAnimZ = new DoubleAnimation();
18: dAnimZ.By = deltaZ;
19: dAnimZ.Duration = new Duration(TimeSpan.FromSeconds(1));
20: Storyboard.SetTargetProperty(dAnimZ, new PropertyPath("(UIElement.Projection).(PlaneProjection.LocalOffsetZ)"));
21: Storyboard.SetTarget(dAnimZ, elem);
22:
23: sb.Children.Add(dAnimX);
24: sb.Children.Add(dAnimY);
25: sb.Children.Add(dAnimZ);
26:
27: return sb;
28: }
Storyboard虽然可以使用Blend很好的获得,但是采用编程的方式会更加好控制以及好重用
这里我采用了每个图片的动画的时间不同来制造一种良好的时间差效果来增强用户体验度
double seconds = 1.0 + index * 0.03;
sb = AnimateStory(img, seconds, -70, 10, 10);为了响应键盘,我也做了一些处理,如下
private IPhotoGallery photoGalley;
public MainPage(string apiKey)
{
InitializeComponent();
photoGalley = new FlickrGallery(apiKey);
this.KeyDown+=(sender,e)=>
{
switch (e.Key)
{
case Key.Enter: SearchImagesInfo(); break;
case Key.Right: if (isAnimateCompleted) { MoveImages(); } break;
}
};
}当按下Enter键后将开始搜索图片信息 而按下右向箭头->将开始切换图片 大家可能注意到了MainPage带了个参数apiKey(一般来说默认的xaml.cs文件的类初始化时不带参数) 这里为了解耦合(不硬编码),采用外部输入Flickr API Key来实现 于是我们通过InitParameters来从asp.net页面中传递参数过来
<asp:Silverlight ID="SL" runat="server" Source="~/ClientBin/FlickrBrowser.xap" InitParameters="FlickrKey=539ddb5257617b1c7045c4539df97337"
MinimumVersion="3.0.40307.0" Width="100%" Height="100%" />
接下来就可以在App.xaml.cs文件中Parse出初始化的参数,并传递给MainPage
private void Application_Startup(object sender, StartupEventArgs e)
{
string flickrKey = "";
if (e.InitParams.ContainsKey("FlickrKey"))
{
flickrKey = e.InitParams["FlickrKey"];
}
this.RootVisual = new MainPage(flickrKey);
}代码下载: