Friday, October 17, 2008

WPF Cover Flow Tutorial : Part 4

We can improve the HCI from Part 3 with some mouse handling.

We add a MouseDown handler method to our Viewport3D :
private void OnViewportMouseDown(object sender, MouseButtonEventArgs e)
var rayMeshResult = (RayMeshGeometry3DHitTestResult)VisualTreeHelper.HitTest(viewPort, e.GetPosition(viewPort));
if (rayMeshResult != null)
for (int i = 0; i < coverList.Count; i++)
if (coverList[i].Matches(rayMeshResult.MeshHit))
This method uses the VisualTreeHelper to find which mesh was hitten by the mouse click. If there is a match, we update the index like we did before. Matches are found with this new Cover method :
public bool Matches(MeshGeometry3D mesh)
foreach (GeometryModel3D part in modelGroup.Children)
if (part.Geometry == mesh)
return true;
return false;
We also modify the TestWindow constructor to load all the pictures from a sample folder :
public TestWindow()
var imageDir = new DirectoryInfo(@"c:\_covers");
int doneImages = 0;
foreach (FileInfo image in imageDir.GetFiles("*.jpg"))
var cover = new Cover(image.FullName, doneImages++);
Mouse click may increment or decrement the index by more than one cover. That's wy we need to fix the UpdateIndex method :
private void UpdateIndex(int newIndex)
if (index != newIndex)
int oldIndex = index;
index = newIndex;
if (index > oldIndex)
for (int i = oldIndex; i <= index; i++)
for (int i = oldIndex; i >= index; i--)
camera.Position = new Point3D(.2 * index, camera.Position.Y, camera.Position.Z);
It is now time to play with more covers. Let's try the current application with a sample folder containing 500+ covers. These covers are usually between 400x400 and 500x500 pixels. The next Process Explorer capture shows that the application needs a lot of cpu and memory resources :
  • Loading all covers brings the cpu to 100%.
  • Browing all covers with the Right key pressed also needs a lot of cpu. Moreover, this rises the cache size from 300 MB to more than 1 GB.
It is possible to improve performance if we use thumbnails for all covers. We will store thumbnails in a subfolder.
private ImageSource LoadImageSource(string imagePath)
var imageFile = new FileInfo(imagePath);
var thumbnailDir = new DirectoryInfo(Path.Combine(imageFile.Directory.FullName, "tn"));
if (!thumbnailDir.Exists)
var thumbnail = new FileInfo(Path.Combine(thumbnailDir.FullName, imageFile.Name));
if (!thumbnail.Exists)
Image source = Image.FromFile(imageFile.FullName);
int height = source.Height;
int width = source.Width;
int factor = (height - 1) / 250 + 1;
int smallHeight = height / factor;
int smallWidth = width / factor;
Image thumb = source.GetThumbnailImage(smallWidth, smallHeight, null, IntPtr.Zero);
return new BitmapImage(new Uri(thumbnail.FullName, UriKind.RelativeOrAbsolute));
Thumbnails improve performance while browsing, but it is still slow at startup. We will try to improve this later.

Continue with Part 5.
Edit 2014-02-23 : Code has moved to github.


Anonymous said...

I thought I would leave my first comment. I don't know what to say except that I have enjoyed reading. Nice blog.

andrea chiu said...
This comment has been removed by a blog administrator.
Cindy Dy said...

I love surfing and blogging. Thanks for your blogs, I have now another idea what to do on my own blogs. Keep it up. I'll come back later to read more post from you. Thank you.