65.9K
CodeProject 正在变化。 阅读更多。
Home

Picasa 和 .NET 3.5 插件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.27/5 (4投票s)

2013年3月12日

CPOL

4分钟阅读

viewsIcon

19241

使用 .NET 3.5 的新功能迁移插件。

介绍 

2008 年初,我用 C# (运行时版本 2.0) 为博客工具 Windows Live Writer 开发了 Picasa 插件。几年过去了,下载量很高 (目前从微软页面 http://plugins.live.com/writer/detail/picasa-image-plugin 下载了 45,000 次),我决定使用 .NET 3.5 的新运行时功能来实现插件的新版本。最终,新插件应该具有一流用户界面的外观和感觉。

背景 

因此,我快速深入研究了 WPF 的新功能、其数据绑定功能以及 LINQ 和 lambda 表达式的强大功能。

WPF 和新用户界面

我一直很欣赏苹果产品的风格:简洁、用户友好,而且总能正常工作 (我意识到苹果也有 bug,但不知何故,它们不像微软用户界面中的 bug 那么显眼)。我一直为属于微软社区而自豪,我的目的是实现一个 UI,它

  1. 快速而简单,并且
  2. 看起来很棒——当然是往好的方面说 :)

我从新用户界面的开发开始,这部分很简单。XAML 部分很容易设置,不包含业务逻辑。但当我开始实现工作线程时,我很快就陷入了一个问题:如何在不同线程加载数据时更新 UI。尽管我知道只有 UI 线程才能更新用户界面上的控件,但我一开始并没有成功让用户控件的数据绑定正常工作。

当 UI 线程试图访问由我的后台工作类实现并从另一个 (异步) 线程填充了对象的集合时,应用程序崩溃了。原因是存在重叠:当后台工作程序填充集合时,由于数据绑定机制,用户界面试图访问该对象集合。感谢 Quantum Bit Designs 和关于 WPF 跨线程集合绑定的文章,我找到了解决这个问题的最佳方法。下图展示了此方法的原理:

首先,后台工作程序执行一个异步操作 (步骤 1)。

BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += new DoWorkEventHandler(WorkerDoWorkOC);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(WorkerRunWorkerCompleted);
worker.RunWorkerAsync();

UI 控件绑定的可观察集合不被工作线程访问。工作线程填充另一个对象集合 (内部列表),该列表需要通知可观察集合所有对象都可供显示 (步骤 2)。现在 WPF 的数据绑定机制发挥了作用 (步骤 3)。

接口 INotifyPropertyChanged 负责通知控件当前绑定到并需要实现的数据。我们只需要调用 OnPropertyChanged(<propertyName>) 方法,并将 UI 绑定的属性名作为参数传递。如果我们这样做,UI 将会崩溃。原因是只有创建 DispatcherObject 的线程才能访问该对象。通过将 UI 控件的调度器对象传递给后台列表,使用异步方法调用了调度器。

this._dispatcher.BeginInvoke(DispatcherPriority.Background,  
          new AddCallback(AddAlbumFromDispatcherThread), albums);

AddAlbumFromDispatcherThread 方法中,会引发 PropertyChanged 事件。这里有一篇关于调度器的精彩文章:http://www.switchonthecode.com/tutorials/working-with-the-wpf-dispatcher - “处理 WPF 调度器”。在 UI 及其绑定的数据元素都由正确的线程通知后,一切都按设计进行。现在用户界面是异步更新的,即使用户滚动列表框项,UI 仍然响应。新用户界面看起来是这样的:

L

LINQ

框架 3.5 的另一个强大功能是 LINQ (语言集成查询)。通过 LINQ,可以以标准化的简化方式查询和访问来自不同数据源 (数据库、XML、Web 服务等) 的数据。

在旧插件 (运行时版本 2.0) 的第一个实现中,我实现了一个 XML 解析器,它通过各种 if 语句遍历整个文档树来返回数据。我想创建一个查询,并且它应该比逐行解析整个文档更高效。使用 LINQ,可以在单个查询中访问不同命名空间的数据,过滤节点中的特定属性,甚至获取过滤后的 XML 节点中的嵌套数据。LINQ 查询通常会延迟到请求数据时执行。通过调用 ToList<someType> 方法可以强制立即执行。

List<PicasaAlbum>  retVal = (from feed in xmlDoc.Descendants(atom + "entry")
 select new PicasaAlbum {
 User         = feed.Element(photo + "user").Value,
 AlbumID      = Convert.ToInt64(feed.Element(photo + "id").Value),
 HasProxy     = hasProxy,
 NumPhotos    = Convert.ToInt64(feed.Element(photo + "numphotos").Value),
 Published    = Convert.ToDateTime(feed.Element(atom + "published").Value),
 AlbumName    = HttpUtility.HtmlDecode(feed.Element(atom + "title").Value),
 AlbumLink    = ( from links in feed.Elements(atom + "link")
                where (string)links.Attribute("rel") == "alternate" 
                select links.Attribute("href").Value).First()
  }).ToList<PicasaAlbum>();

甚至排序也很容易完成。我用 lambda 表达式替换了匿名函数的旧实现。

retVal.Sort(delegate(PicasaImage a1, PicasaImage a2)                    
{
 if (a1.Published < a2.Published)
  return 1;
 else if(a1.Published == a2.Published)
  return 0;
 else
  return -1;
});

lambda 表达式

items.Sort((a, b) => a.Published.CompareTo(b. Published)); 

总而言之,我大大减少了代码量,创建了一个——至少在我看来——非常舒适且外观相当不错的 UI,并且能够进行实验。

  • WPF
  • WPF 中的数据绑定
  • LINQ 和 lambda 表达式。

如果有人想看到所有这些东西的实际运行,并且碰巧是 Windows Live Writer 的博主,那么这个 Picasa 插件,也称为 **BlogPiccs 2.0**,可以从我的网站 (www.omanno.de) 或我的网店 (http://shop.omanno.de) 下载。这个插件可以与 Windows Live Writer、Picasa 以及 .NET 3.5 一起使用。通过这个插件,您可以管理新的相册和照片,并将 Picasa 帐户中的相册和照片集成到个人博客中。如果您有任何意见,请随时与我联系,任何反馈都将不胜感激。

© . All rights reserved.