WinFS Coding Fun

So I've been playing with WinFS here and there since the PDC.  It's a neat model, with lots left for me to explore.  I took the WinFSThumbnailer app (C:\Program Files\Microsoft SDKs\WinFS\Samples\WinFSThumbnailer) and added some enhancements.

First of all, I added a thumbnail viewer in the app.  Seeing the filename in a listbox isn't all that useful if the names came straight from the camera (DSCN0923.JPG, anyone?).  Now as you change focus, I display a thumbnail below the list.

Second, I added support for the EXIF Orientation tag.  This little-used/little-known tag allows you to specify the orientation of the image.  It's actually a way of doing lossless rotation, except that Windows built-in viewer doesn't respect it.  The way it works is, you tilt the camera sideways when you take the image, then the camera detects that and stamps a value (like 8) to indicate that the image is rotated 90°.  Then viewer apps are supposed to automatically rotate the image.  Otherwise you hit “Rotate” in the app, which causes the image to be decompressed from JPEG, rotated, then recompressed to JPEG (extra lossy step).  Since my camera (Nikon 5700) doesn't have a rotation sensor, I thought it would be good to update the tag manually.  Easy with WinFS!

The thumbnail support is pretty easy.  Drag a PictureBox control to the form (don't forget to set SizeMode to StretchImage), then bind the SelectedValueChanged event to the following method:

private void searchResults_SelectedValueChanged(object sender, EventArgs e)
{
  
BindablePhoto pic = (BindablePhoto)searchResults.SelectedItem;

   if
(pic != null
)
  
{
      if (pic.Thumbnail == null)
     
{

         WinFSData wfsDat = new WinFSData(@"\\localhost\DefaultStore\Test Folder");
         using (wfsDat)
         {
  
         
// Retrieve this image from the store
            
Photo photo = (Photo)wfsDat.GetItemById(pic.PictureId);
            
photoPreview.Image =
Image.FromStream(photo.BackingFile.OpenRead());
            
pic.Thumbnail = photoPreview.Image;
         
}
      }
     
else photoPreview.Image = pic.Thumbnail;
   }
}

First, it takes the selected item from the list and casts it to BindablePhoto.  Then it verifies that there is an item, and checks to see if a thumbnail has already been cached (more on this in a moment).  If not, the underlying Photo object is retrieved from the WinFS store, then the backing file is read into an Image and assigned to the PictureBox object.  As you may have noticed, the BindablePhoto object needs to have a new property added: Thumbnail.

In the BindablePhoto class, add the following declaration:

private WeakReference thumbnail;

Then add the following property definition:

public System.Drawing.Image Thumbnail
{
    get
    {
        if (thumbnail != null
&& thumbnail.IsAlive)
        {
            return (System.Drawing.Image
)thumbnail.Target;
        }
        else return null
;
    }
    set { thumbnail = new WeakReference(value
); }
}

Now, what is this WeakReference type, you may be asking?  Good question!  This is a cool trick that I was aware of since it was in Java 1.2 as well.  A WeakReference object is a container for objects that you don't mind losing.  Huh?!  Well, look at our use case.  We want to cache thumbnails to speed up the UI.  What happens when we have 1000 images in the list though, and memory becomes tight?  We don't want to manage our cache ourselves and worry about deleting objects.  So we delegate this to the WeakReference object.  If memory is low, the underlying object is garbage collected (as long as it isn't referenced anywhere else).  Hence, when we need to get the property, we must check the IsAlive property to see if the object is really there.  It's quite easy to use, and offers enormous benefits.

This went longer than I expected.  I'll do another entry soon to show the EXIF Orientation support.

Happy Coding!

Update: Changed minor grammar, and added quotes within the last snippet.

Update #2: Well I'm dumb.  :-(  I checked the SDK documentation and it turns out that you should treat the WinFSData object just like a database connection.  In other words, tear it down every time and let the connection pool worry about efficiency.  I've updated the code above to address this.

posted @ Tuesday, September 20, 2005 9:55 AM

       Print
Comments have been closed on this topic.
«September»
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789