Introduction

The Exif Compare Utility is an application I wrote for my specific
needs. It is a WinDiff equivalent for image files that compares the Exif
meta-data and displays the differences and similarities. The application is
written using WPF and MVVM, and also makes use of my
ExifReader
library. In the article I will briefly explain how to use the application and
also discuss some of the interesting code implementation details.

Code Formatting

The code snippets in the article body have been ruthlessly word wrapped so
they fit within the 600 pixel width requirement that Code Project has. The code
formatting in the actual source files is way more pleasant.

Using the utility

Choose Compare Files either from the menu or from the toolbar, and
you'll get two File Open dialogs which'll let you choose two image files. Once
you choose the files you will see the Exif comparison results (see Figure 1) via
four collapsible sections.

Differing Exif properties

Exif properties only in left image

Exif properties only in right image

Identical Exif properties

Use Clear Files (menu/toolbar) to clear the selections. You can right
click on an entry to bring up the context menu (see Figure 2) which has just one
entry - a Copy command that copies a textual representation to the
clipboard.

Interesting coding details

The WPF application was written using VS 2010 RC and uses the MVVM pattern.
It uses my ExifReader library for reading the Exif data, but implements
the comparison in the model class. I will be referring to a couple of classes
from the
ExifReader
library, and to avoid repetition I will not describe them in this article. Those
of you who want to look it up can read the
ExifReader
article. Note that the source download for this article does include the
ExifReader project too, so you won't need any additional downloads to
compile and run the application.

Model

There is a class called ExifPropertyComparison which represents
a particular Exif property. It will include either the left or right values, or
both depending on whether a property exists in both images or just one.

) are provided for data binding convenience. Some of you may
be wondering if this breaks the MVVM model, since we have properties in a
Model class purely for the View's convenience. But remember that the
View has absolutely no idea of this Model class. The data
association is done at run-time by the WPF data-binding framework. You could
move this class to the View-Model too if you are that pedantic about these
things, but I personally prefer to choose the simplest option to more
doctrinaire and convoluted ones.

The property comparison is based on comparing the Exif tag as well as the
Exif property value. Since both the ExifReader and

ExifProperty

classes do not support equality checks, I implemented the
following comparer classes.

Comparing the Exif value needs a little more code since there can often be
multiple values. Once the comparers are available, writing the actual Exif
comparison code is fairly easy, with a bit of LINQ usage. This is implemented in
the ExifCompareModel class.

The class exposes four properties of type List<ExifPropertyComparison>
representing the four categories of properties - those properties only in either
the left or the right image, those in both images and with identical values, and
those in both but with different values.

The View-Model exposes four ObservableCollection<ExifPropertyComparison>
properties which it populates from the equivalent properties returned from the
Model. The preview image views have their own View-Model and these are returned
via the LeftImageUserControlViewModel and

RightImageUserControlViewModel

properties. Notice the code that brings up
the About dialog, where the Owner window is set through the
command-parameter argument. I'll talk about this later when I show the
corresponding View code.

Theoretically I could have put all this in the main view-model, but I did
this for convenience and for simpler code organization. In addition, there's a
View-Model class for the About dialog where information is shown based on the
assembly's version information. I used a fairly common MVVM technique of raising
an event in the View-Model that's handled by the View to close the About dialog.
People like Josh Smith have blogged and written about this technique in various
articles. This is in contrast to the technique I used to pass the owner

Window

to the code that brings up the About dialog. I could have used the same
technique here, but I chose to do it this way to get a feel for either
techniques. The negative in this latter approach is that you need to have code
in the View class, which may offend MVVM purists, though strictly speaking the
only thing that code does is close the dialog. With the former technique there's
no code needed in the View, but it's some rather inelegant binding code that's
used to pass the Owner window as a command parameter, which some people may not
want to do. Here's the code for the About dialog View-Model.

Look at the highlighted code where the tool-tip is setup. Initially I didn't
have code that looked like that, instead I merely bound to the current object so
I would just see the ToString result of the

ImageUserControlViewModel

object. I found that this only worked the first
time the code was called, and when I opened fresh files, the tool-tip did not
update. It took me a while and some Googling before I realized that the
ToolTip will not inherit the data-context since it was not part of the visual
tree. So in this case, the PlacementTarget will be the containing
StackPanel and I bind its Tag property to the

ImageUserControlViewModel

object, which is what the tool-tip gets
too when it pops up and invokes data-binding. Problem solved.

Here's the Xaml for the main window. Some of the word wrapping where a
binding string is wrapped may actually break the code. But then I don't expect
anyone to copy/paste this code into an IDE - since you can always look at the
provided source download.

I've used my
SystemMenuWindow class to add the About menu entry to the system window. But
the interesting code there is the CommandParameter binding. I find
the first Window ancestor and pass that to the command, since the About dialog
needs to set an owner window for centering. This way we avoid a back-reference
to the View from the View-Model, even though there are MVVM implementations
where the View-Model has a reference to the View. I could also have implemented
an event in the View-Model that the View handles to pass in an owner, but I
thought this was simpler to do.

The property comparisons are shown using styled and templated list-boxes.
They are all wrapped inside Expander blocks, and the Expander
is also styled differently since the default look did not look natural - given
the rest of the styling/theming. I'll briefly go through some of the styles and
templates that are defined in a separate resource dictionary.

Styles and Templates

For customizing the Expander control, I started off with this
MSDN example.
I removed some of the bits (example, those that dealt with the disabled state)
and then customized it to my preference.

Here's the data template for the ListBoxItem - this is where we
actually display the property comparison data. There are some Exif properties
where the ToString implementation in the ExifProperty
class returns a display string of hex bytes. This resulted in horizontal
scrolling, and to avoid this I make sure using a binding that the MaxWidth
is equal to the present width of the ListBox. That's the
highlighted code on top.

The data triggers show how the IsLeftEmpty and

IsRightEmpty

properties are used to change the opacity based on whether a
property is available or not. This was what I was referring to earlier when I
discussed the Model implementation.

This was another piece of code where I stumbled for a few minutes. The

ListBoxItem

's data context will be the ExifPropertyComparison
object associated with it. Since I wanted to bind to a command in the
View-Model, I had to first fetch the correct data source by using

FindAncestor

to lookup the data context associated with the ListBox
(which will be the View-Model instance).

Conclusion

I guess there wasn't anything earth shattering in this article, but I was
primarily trying to discuss issues I ran into and how I solved them. Obviously
if you think there are better ways to solve these issues, please do use the
article forum to put forward your suggestions, criticism, and ideas. Thank you.

History

April 10, 2009 - Article first published

License

Share

About the Author

Nish Nishant is a Principal Software Architect based out of Columbus, Ohio. He has over 17 years of software industry experience in various roles including Lead Software Architect, Principal Software Engineer, and Product Manager. Nish was a Microsoft Visual C++ MVP between 2002 and 2015.

Nish is an industry acknowledged expert in the Microsoft technology stack. He authored C++/CLI in Action for Manning Publications in 2005, and had previously co-authored Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his WordPress blog. Nish is vastly experienced in team management, mentoring teams, and directing all stages of software development.

Contact Nish : If you are interested in hiring Nish as a consultant, you can reach him via his google email id voidnish.

I just chose different shades of Green. So for example, in the comparison grid, there are 3 columns with the middle column in a bolder green. What would you have changed there? Made the left and right columns an even lighter shade?

Firstly, what is the focus? Image and Data! (So what is important?)
The rest is just a presentation framework, & as such should not detract from the aim.

What I'm saying is you are making a fundamental error of making everything compete.
You have strong bold vibrant colours everywhere. Less is more!!!
So back of the bold colours & use thin strong borders to make the frame for your images & data.

Convert to GreyScale with your designs, as this tests for basic colour blindness.

Interesting. So design in color, and periodically see what it looks like in grayscale, and it it's really hard to view in grayscale, re-design the colors in the original. I never thought of that before. Thanks again, Alan.

I brought that to the attention of CP, not going to shame who did it...

As they used Red as a highlight circle, on a Green background in the website.
Which happens to be the most common colour blindness, & affects about 10% of the population.
When i showed CP the GreyScale version, you could not see any contrast difference between the Red & Green.

So good design is always about contrast values!

You will almost never see me go against this principle...
And always remember, less is more!!!

Think I should probably blog/Tip this, but it seems so simple to me...