Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

A plugin for Picasa and .NET 3.5

4.27/5 (4 votes)
4 Apr 2013CPOL4 min read 18.6K  
Plugin migration with new features of .NET 3.5.

Introduction 

In the beginning of 2008 I developed the Picasa plugin for the blogging tool Windows Live Writer in C# (runtime version 2.0). After several years and many downloads (up to now 45,000 downloads from the Microsoft page http://plugins.live.com/writer/detail/picasa-image-plugin), I decided to implement a new version of the plugin using the new runtime features from .NET 3.5. In the end the new plugin should have the look and feel of a state-of-the-art user interface.

Background 

With that in my mind I quickly jumped into the new features of WPF, its databinding possibilities, and the power of LINQ and lambda expressions.

WPF and the new user interface 

I always admired the way of Apple products: simplicity, user-friendliness, and the fact that it always worked (I’m aware of the fact that Apple has its bugs too but they are in some way not that present as the ones in Microsoft user interfaces). I was always proud to belong to the Microsoft community and my intention was to implement a UI, that is:

  1. fast and simple, and
  2. looks like hell – in a good way of course :)

I started with the development of the new user interface and this was the easy part. The XAML parts were easy to setup and contained no business logic. But when I started to implement the worker thread I soon got stuck in the problem on how to update the UI while loading the data in a different thread. Although I knew that only the UI thread itself is allowed to update the controls on the user interface I didn’t manage it in the first place to get the databinding of the user controls working.

The app crashed while the UI thread wanted to access the data collection which my implementation of the background worker class filled with objects – from a different (asynchronous) thread of course. The reason for it was an overlap: while the background worker was filling the collection the user interface tried to access that object collection because of the databinding mechanism. Thanks to Quantum Bit Designs and the article about  WPF cross-thread collection binding, I found the best approach to fix this problem. The following diagram shows the principles of this approach:  

Image 1

At first the background worker executes an asynchronous operation (Step 1).

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

The observable collection which the UI controls are bound to isn’t accessed by the worker thread. The worker thread fills another object collection (internal list) that needs to inform the observable collection that all objects are available for display (Step 2). Now the databinding mechanisms of WPF come into play (Step 3).

The interface INotifyPropertyChanged owns the task to inform controls which data is bound to and has to be implemented. All we need to do is raise the OnPropertyChanged(<propertyName>) method and pass as parameter the name of the attribute the UI is bound to. If we do that the UI will crash. The reason for it is that only the thread that created a DispatcherObject may access that object. By passing the dispatcher object from the UI control to the background list, the dispatcher is invoked using the asynchronous method:

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

In the AddAlbumFromDispatcherThread method the PropertyChanged event is raised. A great article about the dispatcher can be found here: http://www.switchonthecode.com/tutorials/working-with-the-wpf-dispatcher - “Working with the WPF dispatcher”. After the UI and its bound data elements have been informed by the right thread everything works as designed. The user interface now gets updated asynchronously and the UI is responsive even if the user scrolls through the listbox items. The new user interface looks like this:

Image 2

Image 3L

LINQ 

Another powerful feature of the framework 3.5 is LINQ (language-integrated query). With LINQ there is a standardized and simplified way to query and access data from different sources (database, XML, web services, …).

In the first implementation of the old plugin (runtime version 2.0) I implemented an XML parser which returned me the data by traversing the whole document tree with various if-statements. I wanted to create one query and it should be much more performant than parsing the whole document line by line. With LINQ it was possible to access data in different namespaces, to filter out specific attributes in the node, and even to get nested data from filtered XML nodes in one single query. LINQ queries are usually deferred until the data is requested. Immediate execution is forced by calling the ToList<someType> method.

C#
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>();

Even sorting is easily done. I replaced the old implementation of an anonymous function:

C#
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;
});

with a lambda expression:

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

Summing it up, I reduced the code a lot, created – at least in my eyes – a very comfortable and pretty-good-looking UI and was allowed to play with.

  • WPF
  • Databinding in WPF
  • LINQ and lambda expressions.

If anybody wants to see all these things in action and happen to be a Windows Live Writer blogger this plugin for Picasa a.k.a. BlogPiccs 2.0 can be downloaded from my website (www.omanno.de) or my webshop (http://shop.omanno.de) . This plugin works with Windows Live Writer, Picasa, and of course .NET 3.5. With this plugin you can manage new albums and photos, and integrate albums and photos from a Picasa account into personal web blogs. If you have any comments feel free to contact me, any feedback is much appreciated.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)