【原文地址】
Silverlight FlickR Example
【原文发表日期】
25 April 08 11:23
在这个示例中,我将示范在Silverlight客户端通过一个非常简单的调用去访问FlickR的REST API。最终我们将得到一个应用程序,它看上去是这样的:
第一部分,定义部分Silverlight用户界面
第二部分,展示对本机的“打开文件”对话框的支持
第三部分,调用FlickR的服务并找到图片
第四部分,使用隔离存储(IsolatedStorage)在多次运行中保持某些本地设置的值
第五部分,为用户界面添加皮肤
同时欢迎你们获取完整的例子和示例文件。
第一部分,定义部分Silverlight用户界面
开始部分的创建,你可以回头看看我的这篇帖子End to End Silverlight Application post。在Blend里添加一个TextBox和一个Button到窗口中,并如下图所示摆放。
确认你在属性窗口中为它们取了有意义的名称,以便稍后我们以编程的方式访问它们。我给它们起的名字是searchTermTextBox和button。
把一张图片拖入窗口中,我们就有了操作的对象了。(你可以使用SilverlightFlickRDemoFiles压缩包中的cow.jpg)
确认你也为这张图片命名了……我起的名字是searchResultsImage
第二部分,本机的“打开文件”对话框
为了测试一下我们的排版,让我们添加一项功能,弹出“打开文件”的对话框并让它操作客户端的图片。这一点如今你已经可以通过Ajax\HTML轻易地实现。
在page.xaml中,添加一个Click事件处理器
<Button x:Name="button" Width="100" Height="50"
Content="Go"
Click="button_Click"
在page.xaml.cs文件中,实现这个按钮的click事件,调用“打开文件”的对话框。
private void button_Click(object sender, RoutedEventArgs e) { OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "JPEG Files (*.jpg;*.jpeg)|*.jpg;*.jpeg | All Files (*.*)|*.*";
ofd.FilterIndex = 1;
if (ofd.ShowDialog() == DialogResult.OK) {
Stream stream = ofd.SelectedFile.OpenRead();
BitmapImage bi = new BitmapImage();
bi.SetSource(stream);
searchResultsImage.Source = bi;
stream.Close();
}
}
这段代码会打开系统的“打开文件”对话框,允许用户选择某个磁盘上的文件。开发者只具有保存这个文件的权限(仅指保存到应用程序中,而非文件的实际路径)。注意我们是如何操作这张客户端图片的。
现在你可以上传一个文件到服务器,或者使用Isolated Storage将它存到本地。不过这已经有些超出了这个示例的范围了,我们要做的仅仅是在flickr中查找一张图片……
第三部分,调用FlickR的服务并找到图片
重头戏现在开始了。我们需要将查询关键字发送到FlickR REST API,并显示查询结果的图片。当用户点击图片时,我们能够显示出结果中的下一张图片。
首先我们需要调用FlickR REST API。为此你需要一个键值(key),你可以免费从FlickR获得……
接着我们要在Silverlight客户端调用这个REST API,让我们定义一个helper方法来完成它。
void LoadPhotos(string topic)
{
string apiKey = "<<get your own >>";
string secret = "<<get your own >>";
string url = String.Format("http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key={1}&text={0}", topic, apiKey, secret);
WebClient flickRService = new WebClient();
flickRService.DownloadStringCompleted += new DownloadStringCompletedEventHandler(flickRService_DownloadStringCompleted);
flickRService.DownloadStringAsync(new Uri(url));
searchTermTextBox.Text = "Calling FlickR...";
}
接下来我们要解析查询结果,你可以查看http://flickr.com/services/api/explore/以了解结果的格式。基本上,结果看上去类似这样:
<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="ok">
<photos page="1" pages="32769" perpage="100" total="3276843">
<photo id="2436622217" owner="22956152@N04" secret="6c8293bb5c" server="2070" farm="3" title="IMG_3492_resize" ispublic="1" isfriend="0" isfamily="0" />
<photo id="2437437876" owner="41848473@N00" secret="97a7e1a066" server="2303" farm="3" title="Eric & Dog" ispublic="1" isfriend="0" isfamily="0" />
</photos>
</rsp>
为此,我们需要做一些Xml解析的工作。所幸有LinqToXml的支持,在Silverlight中实现这一点非常简单,只需添加一个对System.Xml.linq.dll程序集的引用。
现在让我们实现flickRService_DownloadStringCompleted方法。我们首先要做的是一些错误检查……这能够极大地帮助我们确认对FlickR的所有调用都是正确的。
XDocument xmlPhotos = XDocument.Parse(e.Result);
if (e.Error != null ||
xmlPhotos.Element("rsp").Attribute("stat").Value == "fail"){
string results = e.Result;
searchTermTextBox.Text= "Error! (" + results + ")";
return;
}
else {
searchTermTextBox.Text = "It worked!";
}
现在我们只需要调用LoadPhotos方法。
private void button_Click(object sender, RoutedEventArgs e)
{
LoadPhotos(searchTermTextBox.Text);
}
运行应用,如果你看到的是这样的信息,请回头检查一下你的API键值。
如果看到这个,说明一切顺利,已经准备好进入下一步了。
现在,我们需要解析这个Xml结果集,并取出图片的URL。我们将使用Linq的强大功能处理所有这些丑陋的Xml解析工作。所有要做的就是定义一个.NET类,将XML元素映射到它。
public class FlickRPhoto
{
public string Id { get; set; }
public string Owner { get; set; }
public string Secret { get; set; }
public string Server { get; set; }
public string Farm { get; set; }
public string Title { get; set; }
}
随后,让我们为这个类添加一个属性,它将遵守FlickR的URL规则以建立图片对应的URL
public string ImageUrl{ get
{
return string.Format("http://farm{0}.static.flickr.com/{1}/{2}_{3}.jpg",
Farm,Server,Id,Secret);
}
}
现在,我们需要一段Linq代码将Xml元素映射到这个类。
Photos = from photo in xmlPhotos.Element("rsp").Element("photos").Descendants().ToList()
select new FlickRPhoto
{
Id = (string)photo.Attribute("id"),
Owner = (string)photo.Attribute("owner"),
Secret = (string)photo.Attribute("secret"),
Server = (string)photo.Attribute("server"),
Farm = (string)photo.Attribute("farm"),
Title = (string)photo.Attribute("title"),
};
让我们定义一个类字段Photos,以便稍后访问它。
IEnumerable<FlickRPhoto> Photos;
现在我们要做的只是显示图片,只需从返回的结果集中选取第一条数据并显示它!
FlickRPhoto p = Photos.First();
this.searchResultsImage.SetValue(Image.SourceProperty, p.ImageUrl);
searchTermTextBox.Text = p.Title;
这很棒,但我还想看其它的图片……一个简单的方式是在点击图片时改变它的内容。为此定义一个事件处理器
<Image MouseLeftButtonDown="searchResultsImage_MouseLeftButtonDown"
x:Name="searchResultsImage"
然后实现它。我在开头部分做了些错误检查,并用一个叫做ImageNumber的类字段来跟踪当然显示的是哪一张图片。
private void searchResultsImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (Photos == null) return;
if (ImageNumber >= Photos.Count()) ImageNumber = 0;
FlickRPhoto p = Photos.Skip(ImageNumber).First();
this.searchResultsImage.SetValue(Image.SourceProperty, p.ImageUrl);
ImageNumber++;
}
现在当你点击图片时,它就会在结果集中循环显示所有图片。
第四部分,使用隔离存储(IsolatedStorage)在多次运行中保持部分本地设置的值
现在,让我们尝试在多次运行时保持部分状态的值。
首先,让我们在“Go”按钮被按下时,记录下textbox的结果。
private void button_Click(object sender, RoutedEventArgs e)
{
LoadPhotos(searchTermTextBox.Text);
ApplicationSettings.Default["searchTerm"] = txtBox.Text;
ApplicationSettings.Default.Save();
}
在图片改变时,也是依样画葫芦
private void searchResultsImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (Photos == null) return;
if (ImageNumber >= Photos.Count()) ImageNumber = 0;
FlickRPhoto p = Photos.Skip(ImageNumber).First();
this.searchResultsImage.SetValue(Image.SourceProperty, p.ImageUrl);
searchTermTextBox.Text = p.Title;
ApplicationSettings.Default["imageNumber"] = ImageNumber;
ApplicationSettings.Default.Save();
ImageNumber++;
}
随后在应用再次启动时,我们就可以找到最近一次的状态值,并用它初始化应用。
public Page() {
InitializeComponent();
if (ApplicationSettings.Default.Contains("searchTerm")){
this.txtBox.Text = (string)ApplicationSettings.Default["searchTerm"];
button_Click(null, null);
}
if (ApplicationSettings.Default.Contains("imageNumber")){
ImageNumber = (int)ApplicationSettings.Default["imageNumber"];
}
}
在你第一次运行它的时候,因为没有任何状态值被记录,会自动使用默认值。但当你再次运行它的时候,你就会注意到它将使用你上次所保留的值!
第五部分,添加皮肤
现在,让我们给它一个好看的皮肤。我将再次使用Corrina那个风格粗犷的皮肤。
只需从Corrina的示例中剪切、粘贴<ApplicationResources>这一部分到你的App.Xaml中
并添加样式
<Button Style="{StaticResource buttonStyle}"
<TextBox Style="{StaticResource textBoxStyle}"
就大功告成了!
同时欢迎你们获取完整的例子和示例文件。