在Silverlight 1.1的时候,我就像用Silverlight设计一个饭否Show,无奈1.1时代貌似只能通过Web Services来获取跨域的数据,我认为这是一种别扭的开发方式,再加上1.1对中文并不友好,所以就改用Flash实现了这个饭否Show。
Silverlight 2终于解决了上述的两个问题,支持跨域获取数据,支持中文显示,于是我迫不及待的尝试着开发了一个饭否Show(在Silverlight 2 beta SDK 发布的几日后开始开发,开发完成后便忘记了,-___-|||),下面来简要的分享一下开发过程,也望各位Silverlight前辈不吝指教。
UI UI的界面如下图所示:
UI采用Blend 2.5和Design设计。
如果设计简单的Silverlight界面,那么Blend足矣,但稍复杂的,比如上图中的波浪渐变效果,Blend就无能为力了,而这正是Design的拿手本领。
Design的使用方法就不介绍了,设计完毕后,导出为Silverlight画布,然后我们copy需要的XAML节点即可。
具体的XAML内容就不在此展示了,文末我提供了本文的代码下载,有兴趣的朋友可以下载查看。
饭否API 从上图来分析,我们只用到了饭否中的两部分信息:我的信息和我的消息。
在
饭否API 中,获取这两部分数据的方法如下:
获取用户信息:
http://api.fanfou.com/users/show/
用户ID .xml
获取用户消息:
http://api.fanfou.com/statuses/user_timeline/
用户ID .rss
那么根据饭否提供的API,我编写了下面两个类:
饭否API类
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.IO; using System.Linq; using System.Xml; using System.Xml.Linq; using System.Collections.Generic; namespace SilverlightFanShow { /**/ /// <summary> /// 饭否用户 /// </summary> public class FanfouUser { /**/ /// <summary> /// 用户头像的URL /// </summary> public Uri ImageURL { get ; set ;} /**/ /// <summary> /// 用户的显示名称 /// </summary> public string Name { get ; set ;} /**/ /// <summary> /// 用户的饭否链接地址 /// </summary> public Uri URL { get ; set ;} /**/ /// <summary> /// 从Stream加载饭否用户实例 /// </summary> /// <param name="stream"></param> /// <returns></returns> public static FanfouUser Load(Stream stream) { FanfouUser user = new FanfouUser(); try { XmlReader reader = XmlReader.Create(stream); XElement doc = XElement.Load(reader); user.ImageURL = new Uri(doc.Descendants( " profile_image_url " ).FirstOrDefault().Value); user.Name = doc.Descendants( " name " ).FirstOrDefault().Value; user.URL = new Uri(doc.Descendants( " url " ).FirstOrDefault().Value); } catch { } return user; } /**/ /// <summary> /// 默认构造函数 /// </summary> public FanfouUser() { this .ImageURL = new Uri( " http://avatar.fanfou.com/s0/00/4q/py.jpg?1183096447 " ); this .Name = " Windie " ; this .URL = new Uri( " http://fanfou.com/笑煞天 " ); } } /**/ /// <summary> /// 饭否消息 /// </summary> public class FanfouMessage { /**/ /// <summary> /// 饭否消息的GUID(注:并不是.NET里的GUID) /// </summary> public string GUID { get ; set ; } /**/ /// <summary> /// 饭否消息本体 /// </summary> public string Message { get ; set ; } /**/ /// <summary> /// 饭否消息的发布时间 /// </summary> public DateTime Time { get ; set ; } /**/ /// <summary> /// 从Stream加载饭否消息列表 /// </summary> /// <param name="stream"></param> /// <returns></returns> public static List < FanfouMessage > Load(Stream stream) { List < FanfouMessage > msgList = new List < FanfouMessage > (); try { XmlReader reader = XmlReader.Create(stream); XElement doc = XElement.Load(reader); IEnumerable < XElement > msgs = from item in doc.Descendants( " item " ) select item; foreach (XElement item in msgs) { FanfouMessage msg = new FanfouMessage(); msg.GUID = item.Descendants( " guid " ).FirstOrDefault().Value; msg.Message = item.Descendants( " title " ).FirstOrDefault().Value; msg.Time = DateTime.Parse(item.Descendants( " pubDate " ).FirstOrDefault().Value); msgList.Add(msg); } }catch { } return msgList; } /**/ /// <summary> /// 默认构造函数 /// </summary> public FanfouMessage() { this .GUID = Guid.NewGuid().ToString(); this .Message = " 欢迎使用Windie Chai设计的Silverlight饭否Show。 " ; this .Time = DateTime.Now; } } }
传入参数 其实我设计这个饭否Show并不是只为了给我一个人使用,那么如何让每位朋友都可以把它变成自己的饭否Show呢?
那么必然要使Silverlight能够接受参数。
在做Flash开发时,可以在HTML中通过Object的“FlashVars”参数来向Flash传递参数列表,在Silverlight中我们同样可以这样做。
通过给“
InitParams”参数赋值,我们就可以向Silverlight传递参数了。 给“
InitParams”参数赋值 的方法如下:
Silverlight的HTML代码块
< object type ="application/x-silverlight-2-b1" height ="140" width ="160" data ="data:application/x-oleobject;base64,QfXq3+HzJEysrJnDBxUISgAIAACJEAAAeA4AABAAAAAjAEYARgBGAEYARgBGAAAAAAAAAAAAAAB4AAAAaAB0AHQAcAA6AC8ALwBmAGkAbABlAHMALgBjAG4AYgBsAG8AZwBzAC4AYwBvAG0ALwB4AGkAYQBvAHMAaABhAHQAaQBhAG4ALwBzAGkAbAB2AGUAcgBsAGkAZwBoAHQAZgBhAG4AcwBoAG8AdwAuAHgAYQBwAAAAPAAAAAAAAAAmAAAAbwBuAFMAaQBsAHYAZQByAGwAaQBnAGgAdABFAHIAcgBvAHIAAAAAAAAAAAAAAAAAAAAOAAAASQBEAD0AEXtecSlZAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA==" > < param name ="source" value ="http://files.cnblogs.com/xiaoshatian/silverlightfanshow.xap" /> < param name ="onerror" value ="onSilverlightError" /> < param name ="background" value ="#FFFFFF" /> < param name ="InitParams" value ="ID=笑煞天" /> < param name ="EnableHtmlAccess" value ="true" /> < a href ="http://go.microsoft.com/fwlink/?LinkID=108182" style ="text-decoration: none;" > < img src ="http://go.microsoft.com/fwlink/?LinkId=108181" alt ="Get Microsoft Silverlight" style ="border-style: none" /> </ a ></ object >
仅仅把ID参数传入到Silverlight中是不够的,我们还需要在Silverlight中接收这个参数,并做进一步处理。
我们打开Silverlight项目中的App.xaml,为其Application_Startup事件添加如下代码:
Application_Startup
private void Application_Startup( object sender, StartupEventArgs e) { string id = " 笑煞天 " ; if (e.InitParams.Keys.Contains( " ID " )) id = e.InitParams[ " ID " ]; this .RootVisual = new Page(id); }
要注意的是Page默认并没有带有一个String参数的构造函数,所以我们接下来还需要为Page类添加这一构造函数重载。
Page.xaml.cs 最后,我们开始编写最关键的代码。我将在Page.xaml.cs中添加方法或事件来调用饭否API、获取用户的信息和消息列表、实现上一条下一条的跳转等。
具体的代码并不难以理解,不再详细解释,大家看注释便好:
Page.xaml.cs代码
using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.Net; using System.IO; using System.Xml; using System.Xml.Linq; using System.Threading; using System.Windows.Browser; namespace SilverlightFanShow { public partial class Page : UserControl { // 饭否用户ID private string userID; // 饭否用户 private FanfouUser user; // 当前消息 private FanfouMessage currentMessage; // 消息列表 private List < FanfouMessage > messageList; public Page( string id) { this .userID = id; InitializeComponent(); } /**/ /// <summary> /// 布局加载事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void LayoutRoot_Loaded( object sender, RoutedEventArgs e) { // 加载用户信息 Uri userUrl = new Uri( " http://api.fanfou.com/users/show/ " + this .userID + " .xml " ); WebRequest userRequest = WebRequest.Create(userUrl); userRequest.BeginGetResponse( new AsyncCallback(GetUserInfo), userRequest); // 加载用户消息 Uri msgUrl = new Uri( " http://api.fanfou.com/statuses/user_timeline/ " + this .userID + " .rss " ); WebRequest msgRequest = WebRequest.Create(msgUrl); msgRequest.BeginGetResponse( new AsyncCallback(GetMessages), msgRequest); } /**/ /// <summary> /// 获取用户信息 /// </summary> /// <param name="asyncResult"></param> void GetUserInfo(IAsyncResult asyncResult) { WebRequest request = asyncResult.AsyncState as WebRequest; WebResponse response = request.EndGetResponse(asyncResult); Stream stream = response.GetResponseStream(); user = FanfouUser.Load(stream); // 反映到UI this .pic.SetValue(Image.SourceProperty, user.ImageURL); this .userName.Text = user.Name; } /**/ /// <summary> /// 获取用户消息 /// </summary> /// <param name="asyncResult"></param> void GetMessages(IAsyncResult asyncResult) { WebRequest request = asyncResult.AsyncState as WebRequest; WebResponse response = request.EndGetResponse(asyncResult); Stream stream = response.GetResponseStream(); messageList = FanfouMessage.Load(stream); // 反映到UI if (messageList.Count > 1 ) this .SetMessage(messageList[ 0 ]); } /**/ /// <summary> /// 设置当前消息 /// </summary> /// <param name="message"></param> private void SetMessage(FanfouMessage message) { // 去掉消息正文前边的用户名 string msg = message.Message.Substring(message.Message.IndexOf( ' : ' ) + 1 ); // 如果消息正文过长,截短之 if (msg.Length > 50 ) msg = msg.Substring( 0 , 50 ) + " " ; this .txt.Text = msg; this .time.Text = message.Time.ToShortDateString(); this .currentMessage = message; } /**/ /// <summary> /// 下一条消息按钮按下事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnNext_MouseLeftButtonDown( object sender, MouseButtonEventArgs e) { int msgIndex = messageList.IndexOf(currentMessage); if (msgIndex + 1 < messageList.Count) SetMessage(messageList[msgIndex + 1 ]); else if (messageList.Count > 0 ) SetMessage(messageList[ 0 ]); } /**/ /// <summary> /// 下一条消息按钮按下事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnPrev_MouseLeftButtonDown( object sender, MouseButtonEventArgs e) { int msgIndex = messageList.IndexOf(currentMessage); if (msgIndex > 0 ) SetMessage(messageList[msgIndex - 1 ]); else if (messageList.Count > 0 ) SetMessage(messageList[messageList.Count - 1 ]); } /**/ /// <summary> /// 头像点击事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void goToFanfou( object sender, MouseButtonEventArgs e) { // 打开用户的饭否页面 HtmlPage.Window.Navigate( this .user.URL, " _blank " ); } } }
预览
如果你也想要在你的blog中添加这个Widget,那么,Copy上面的HTML代码,修改ID就可以了。
源码下载
点击下载源码包(包括Visual Studio Solution和Expression Design文件)