Binding to the file system

October 7, 2007

This post demonstrates how to bind an ItemsControl to the files in a directory.  We will see how to use the FileSystemWatcher within a custom DataSourceProvider to update a collection of FileInfo wrappers, and put the INotifyPropertyChanged interface to use for providing visual alerts of when a file changes.

This weekend I built a little app which monitors the files in a directory.  It displays information about each file, and updates the UI when a file is created, changed, renamed, or deleted.  Since the whole app depends on the flaky FileSystemWatcher component it is not the most reliable piece of software in the world, but this is just a learning experience; not production code.  In this post I will review some of the more interesting aspects of how the app works, but feel free to download the source code from the bottom of this page to see the whole thing.

Here is what the app looks like when running it and one file information area is expanded:

FileSysViewer (normal)

When the ‘Secrets.txt’ file is edited and saved, the UI notifies the user by flashing that file’s area, as seen below:

FileSysViewer (changed)

So how does this work?  We can break it down into five parts, and briefly examine each of them.

ObservableFileInfo

You can bind to a System.IO.FileInfo object and display its data in the UI, but a FileInfo is just a snapshot.  If the file on disk is modified after you create a FileInfo snapshot of it, that snapshot is not updated.  Since the purpose of this application is to monitor changes to files in a directory, we need to take another approach.

To work around the static nature of FileInfo, I created a class called ObservableFileInfo.  That class is a FileInfo wrapper which descends from my BindableObject base class.  It exposes a FileInfo property so that the UI can bind to the data about the file.  When the file is changed we assign the FileInfo property a new snapshot of the file, and raise the PropertyChanged event for the FileInfo property.  This informs the binding system that it should reference the new value of the property, with the new snapshot in it.

FileSystemDataProvider

Some class needs to be responsible for generating a collection of ObservableFileInfo objects and updating them when the corresponding files on disk are modified.   That work is handled by the FileSystemDataProvider class.  Internally it uses the FileSystemWatcher component to monitor changes to a directory.  Unfortunately FileSystemWatcher is notoriously unreliable, so expect to see some strange inconsistencies (such as, opening a file sometimes affects its LastAccessedTime and sometimes does not…).

Here is the constructor for that class:

FileSysViewer (FileSystemDataProvider ctor)

When it is time to retrieve the initial list of files in the target directory, this overridden method is invoked:

FileSysViewer (FileSystemDataProvider beginquery)

When the FileSystemWatcher’s events are raised, the event handlers modify the appropriate ObservableFileInfo object in the _files collection.  For example:

FileSysViewer (FileSystemDataProvider onrenamed)

ObservableFileInfoCollection

When a file is created or deleted from the monitored directory, the FileSystemDataProvider will add or remove an ObservableFileInfo from the list of files.  But how does the UI know to add or remove the corresponding visual elements?  The answer lies in the fact that we store the ObservableFileInfo objects in an ObservableCollection<T> subclass called ObservableFileInfoCollection.  That class leverages the collection changed notifications built into ObservableCollection<T> to let the binding system know when the list has changed.  Here is that class:

FileSysViewer (collection class)

ObservableFileInfoTemplate

FileSystemDataProvider exposes a list of ObservableFileInfo objects to the UI.  Since WPF has no idea how to display those objects, we tell it how to do so by providing it a DataTemplate.  That DataTemplate renders an ObservableFileInfo object in an Expander control, like this:

FileSysViewer (template visuals)

Main Window

Lastly we have the Window which contains an ItemsControl which is bound to the list of files.  Most of that Window’s XAML is seen below:

FileSysViewer (window)

Download the demo project here: FileSysViewer (demo project)  Be sure to change the file extension from .DOC to .ZIP and then decompress the file.


Data binding the SplitButton’s ContextMenu

October 1, 2007

Someone recently posted an article to CodeProject which provides a pretty nice implementation of a “split button.” WPF does not have a built-in split button, so this is a solid addition to the standard control library. The original article can be found here.

The author of SplitButton allows you, the developer, to easily populate its ContextMenu by adding child MenuItem elements to the SplitButton in XAML. That looks something like so:

Bound SplitButton (manual)

Using this “manual” approach is fine for many scenarios, but won’t help in others. It is not unusual to populate a menu with items that come from an XML file; like a config file just for that menu. This blog post shows how to bind the SplitButton’s dropdown menu to some XML data.

Here is the XML data we will use to populate the SplitButton’s menu:

Bound SplitButton (xml)

Eventually we want the SplitButton’s menu to look like this:

Bound SplitButton (screenshot)

We can accomplish this by configuring the SplitButton’s ContextMenu to use the XML data as a source for generating its items. This works because SplitButtons uses its ContextMenu as the dropdown shown when you click on the “dropdown button.” Here is the complete declaration of our bound SplitButton:

Bound SplitButton (button declaration)

The SplitButton’s ContextMenu is configured in four ways.

  1. Its ItemsSource is bound to the XML data seen previously. This means that the MenuItems will be created automatically based on the XML data to which it is bound.
  2. The routed Click event of MenuItem is handled by the ContextMenu, so that when the user clicks on any of its MenuItems the same OnXmlMenuItemClick handler in the code-behind will be called.
  3. Its ItemContainerStyle is set to a Style which assigns the command’s ID to the Tag of the MenuItem. The “command” in this sense represents whatever action should occur when the item is clicked, and its ID is used in the code-behind to figure out what to do when an item is clicked.
  4. Its ItemTemplate is set to a DataTemplate which displays the command’s title. This is what you see when you look at a menu item in the dropdown.

The method in the Window’s code-behind, which executes when an item in the SplitButton’s ContextMenu is clicked, is defined like this:

Bound SplitButton (handler)

Download the demo app here: Bound SplitButton (demo project) Be sure to change the file extension from .DOC to .ZIP and then decompress it.  NOTE: This demo project, which I took from the CodeProject article, was built using a beta of Visual Studio 2008.  All modifications were made to the Window1.xaml and Window1.xaml.cs files in the Demo project.