Sacha Barber recently posted an article to CodeProject titled “A Simple WPF Explorer Tree.” The article shows how to lazy-load the TreeView control with the directory structure on your machine. It is a nice gentle intro to lazy-loading the WPF TreeView, and I highly recommend checking it out.
After reading Sacha’s great article I felt that something was amiss. In his demo, the root level items in the tree have a “drive” icon, and all other items have a “folder” icon. The way he implemented the logic which determines which icon to use involves a value converter. A TreeViewItem’s Image element, which displays an icon, has its Source property bound to the Header of the TreeViewItem. That binding has a converter which looks for a backslash character in the header text, and if it finds one that means the item is at the root level (i.e. it represents a drive, not a directory). For example, if the value converter receives “C:\” as the header text, it returns the “drive” icon.
The value converter contains the names of the icon resources, and it actually loads a BitmapSource using those hard-coded resource identifiers. This is what I have a problem with. I think that hard-coding resource identifiers in things like value converters and template selectors should be avoided because it severely limits the reusability of those classes.
There are at least two other ways to go about implementing this icon selection functionality that do not involve sticking resource keys into a value converter. The first option is to allow you to set the icon file names (the resource identifiers) as properties of the value converter. That way you can specify which icons to use in XAML, and the converter is only required to know how to analyze the data it is passed.
I don’t like that option too much. I really don’t think that using a value converter is ideal in this situation. The converter is basing its logic off of the presence of a certain character in the header text, which works just fine, but feels brittle to me. I think that the structure of the TreeView and its items should provide all the information we need, not the arbitrary contents of the data in the items.
The solution I came up with involves applying an attached property to the root level items, and then using DataTriggers to bind against that property and decide which icon to use. Here’s the code. First we have the class with the attached property, note the default value is false:
(Sorry about the weird indentation there, my images can only be so wide…)
When we create the initial set of TreeViewItems, representing the drives on the computer, we set the IsRootLevel attached property on each item to true:
Finally, we have the template which specifies how each TreeViewItem should render its header. Notice that since the header is shown by a ContentPresenter, we need to bind the DataTriggers against a relative source which finds the associated TreeViewItem:
Download the demo project here: ExplorerTree (demo project) Be sure to change the file extension from .DOC to .ZIP and then decompress the file.