风型 WindStyle


Silverlight 2 打造饭否Show

在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,我编写了下面两个类:

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>
 ///
 ///
 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>
 ///
 ///
 public static List Load(Stream stream)
 {
            List msgList = new List();

 try
 {
                XmlReader reader = XmlReader.Create(stream);
                XElement doc = XElement.Load(reader);

                IEnumerable 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”参数赋值的方法如下:

<a href="http://go.microsoft.com/fwlink/?LinkID=108182">
<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none" />
</a>

仅仅把ID参数传入到Silverlight中是不够的,我们还需要在Silverlight中接收这个参数,并做进一步处理。
我们打开Silverlight项目中的App.xaml,为其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、获取用户的信息和消息列表、实现上一条下一条的跳转等。
具体的代码并不难以理解,不再详细解释,大家看注释便好:

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 messageList;

 public Page(string id)
 {
 this.userID = id;
            InitializeComponent();
        }

 /**//// <summary>
 /// 布局加载事件
 /// </summary>
 ///
 ///
 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>
 ///
 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>
 ///
 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 &gt; 1)
 this.SetMessage(messageList[0]);

        }

 /**//// <summary>
 /// 设置当前消息
 /// </summary>
 ///
 private void SetMessage(FanfouMessage message)
 {
 //去掉消息正文前边的用户名
 string msg = message.Message.Substring(message.Message.IndexOf(':') + 1);
 //如果消息正文过长,截短之
 if (msg.Length &gt; 50)
                msg = msg.Substring(0, 50) + "";
 this.txt.Text = msg;
 this.time.Text = message.Time.ToShortDateString();
 this.currentMessage = message;
        }

 /**//// <summary>
 /// 下一条消息按钮按下事件
 /// </summary>
 ///
 ///
 private void btnNext_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
 {
 int msgIndex = messageList.IndexOf(currentMessage);
 if (msgIndex + 1  0)
                SetMessage(messageList[0]);
        }

 /**//// <summary>
 /// 下一条消息按钮按下事件
 /// </summary>
 ///
 ///
 private void btnPrev_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
 {
 int msgIndex = messageList.IndexOf(currentMessage);
 if (msgIndex &gt; 0)
                SetMessage(messageList[msgIndex - 1]);
 else if (messageList.Count &gt; 0)
                SetMessage(messageList[messageList.Count - 1]);
        }

 /**//// <summary>
 /// 头像点击事件
 /// </summary>
 ///
 ///
 private void goToFanfou(object sender, MouseButtonEventArgs e)
 {
 //打开用户的饭否页面
            HtmlPage.Window.Navigate(this.user.URL, "_blank");
        }
    }
}

预览

Get Microsoft Silverlight

如果你也想要在你的blog中添加这个Widget,那么,Copy上面的HTML代码,修改ID就可以了。

源码下载

点击下载源码包(包括Visual Studio Solution和Expression Design文件)

Related posts

31 Comments

  1. nasa 说到:

    不错小建议 代码的折叠功能不是在所有的浏览器下都好用. 建议换一个方式,这样不影响阅读.

    回复回复
  2. 在线代理 说到:

    我想知道这样做,比用flash做,的意义在什么地方。

    这个也是我一直没有装silverlight的原因。看不出来比flash高明的地方。

    回复回复
  3. 航天奇侠 说到:

    sl的高明之处并不是做出比flash更酷的效果,而是对资源的整合利用,将一个很酷的东西商业化的过程.

    回复回复
  4. jejwe 说到:

    看不出来比flash高明的地方,你当然可以不用
    至少sl对.NET开发者友好就是很高明的地方

    回复回复
  5. 在线代理 说到:

    @ 航天奇侠

    flash 也照样可以整合资源啊, 也可以将很cool的东西商业化啊。

    还是 jejwe 说得对,
    主要是对于.net 的开发者��很容易接受。

    等啥时候我不做php了,我也用silverlight,到时候起码应该是silverlight 3了。

    回复回复
  6. 同意jejwe的观点,而且,Silverlight不仅在开发方式上比Flash更为友好(Flash 的Action版本之间的变化以及开发环境都不如Visual Studio强大友好),而且基于.NET Framework的Silverlight有着强大的底层支持,在数据处理等各方面也要比Flash更加轻松。

    回复回复
  7. @李战
    最近果然总是见到你的猴子。

    回复回复
  8. nasa 说到:

    @李战
    @Windie Chai(笑煞天)
    是啊 经常看到….

    回复回复
  9. Icyflash 说到:

    点击头像,widget就消失了…
    IE7.MAXTHON2,FIREFOX2下均这样

    回复回复
  10. sumh 说到:

    呵呵,不错啊,最近研究去sl啦

    回复回复
  11. phantaci 说到:

    TW 里点击也会消失.

    回复回复
  12. adrianhhhhh 说到:

    Windie哈哈。。

    回复回复
  13. @Icyflash
    @phantaci
    这也是我感觉到奇怪的地方,点击头像的动作应该是在新窗口打开用户的饭否页面,在本地调试时也是没问题的,各位可以下载源码看一下,但我不明白为什么上传之后就失效了。

    回复回复
  14. 二手的程序员 说到:

    废话少说,马克一下

    回复回复
  15. Icyflash 说到:

    引用HTML加上这句点击头像就可以弹出页面了
    <param name="EnableHtmlAccess" value="true" />

    回复回复
  16. 包建强 说到:

    你快把WF翻译完,先别研究别的技术,搞���来烂尾楼就不好了,会被人鄙视的!!!!!

    回复回复
  17. @包建强
    呵呵,放心吧,我会翻译完的。

    回复回复
  18. @Icyflash
    谢谢兄台指点!

    回复回复
  19. paylasim 说到:

    very nice blog… :)

    回复回复
  20. 时代 说到:

    为什么点击就消失呢��� 不解!!!

    回复回复
  21. @时代
    缺了一行代码…已经修正了呀?

    回复回复
  22. 时代 说到:

    不清楚什么原因,Microsoft Silverlight
    Version: 2.0.30226.2

    ��时候打开网页,就看不到加载完头像,点击就消失,有时候清空缓存,反复刷新,就能看到头像,点开开新网页

    回复回复
  23. @时代
    可能没有获取到饭否的数据吧.
    不过我已经做过处理了,不应该啊…..

    回复回复
  24. 时代 说到:

    清空缓存刷新,图像就能出来,再就是点 两边按钮,文字并不切换啊,总是显示 3-25 的那句话

    回复回复
  25. @时代
    也可能是你的网络有些问题,以至于饭否API调用失败。

    回复回复
  26. Alanchin 说到:

    各位高手们!
    请你们帮个忙,你们能否开发一个可以非常灵活的建立多及审��的工作流的软件,但是一定是支持MOSS软件的,又可以很容易的管理好人员组织架构。做到电子文档签名的。这样的软件我们很期待。谁有消息都可以和我联系。
    QQ:190922833
    有酬谢!

    回复回复
  27. ohwhy 说到:

    请问为什么我都装了最新版的SL,你的这个为什么还是提示安装呢?

    回复回复
  28. @ohwhy
    因为这个使用旧版做的。

    回复回复
  29. [...] BitmapImage bmp = new BitmapImage(); bmp.UriSource = new Uri(cover); this.img_Cover.Source = bmp; 3.另外,之前基于beta1制作的饭否widget无法编译通过了,在异步调用的方法中,似乎无法直接操作控件的属性了,不知何解。 [...]

Leave a Reply