To demonstrate the use of the FileSystemWatcher class, I’m going to create a WPF application
that shows you any new pictures the user adds to their Pictures folder. First, I’m going
to resize the window a bit so that it is more the shape of a normal picture, and I’ll
give the window a name. Let’s call it MainWindow. Next, I’m going to go into the toolbox and
drag over an Image control. I just want it to take up the entire MainWindow, so I’ll
just remove the margins. Let’s call it updatedImage.
We want to create an event handler using the FileSystemWatcher class, which allows us to
monitor a particular folder for changes. The FileSystemWatcher class is contained in the
System.IO namespace, so I’ll add that now. First, I’ll need to identify the user’s
Pictures folder. I’ll add it to a string called watchedFolder. I’ll use the Path.Combine
method to concatenate the user profile folder, which is an environment variable, and the
Pictures sub-folder. To get an environment variable, you call Environment.GetEnvironmentVariable.
Then, I’ll specify the environment variable USERPROFILE. You can identify all environment
variables at a command prompt by running the command SET.
Now I’m going to combine the user’s profile with the sub-folder Pictures, which in Vista
is the subfolder for anyone’s pictures. Now I just need to create a FileSystemWatcher
instance for that path. I’ll call it fsw, and I’ll provide the path as part of the
constructor. To demonstrate how to interact with WPF controls, I’ll set the window’s
title to the path that we’re monitoring. That way, the user will be able to see that
we’re monitoring the correct folder.
FileSystemWatcher has a couple of properties that we want to specify. Often, if you’re
unloading pictures from a camera, it will load it into sub-folders of the Pictures folder,
so I want to make sure we monitor subfolders as well. I’ll set the IncludeSubdirectories
property to true. I also want to set the NotifyFilters property so that updates are provided for
a couple of different types of edits. You can see here that I can have it notify me
about a couple of different properties of a file:
· Attributes filters are triggered when a file’s attributes are edited.
· CreationTime changes if someone manually went in and edited the creation time or if
a new file were created.
· DirectoryName would occur if a file was moved.
· FileName would occur if a file were renamed.
· LastAccess and LastWrite would trigger the filter if a file were updated.
· If a file were appended or shortened, the Size property would change.
· If a security descriptor were changed, the Security property would be used.
For me, I’m going to trigger on FileName and Last Write. That should be sufficient
to notify me if a file is added.
Now that I’ve configured the FileSystemWatcher, I’m going to create a handler for the Changed
event. You can see that in C# I specify it with the += operator. It provides me with
a default handler—I just need to press Tab to insert it and it generates it for me. Again,
it prompts me to press Tab to generate the event handler. I press Tab again, and I have
an event handler.
Now I need to write the event handler; I can remove the default exception because I’m
going to implement it now.
There’s a trick I need to explain. I’m need to display the updated picture in this
control, however, when you’re handling events in a WPF application, the event handler is
in a different thread. That means that I don’t have direct access to the controls in the
window. So, I could write this command and specify the path of the updated file,
but it won’t work—I would get an exception because I don’t have access to the controls
in the WPF window. What I need to do instead is to use the dispatcher.
You can see that I have access to the updatedImage control. I call the Dispatcher.Invoke command
which takes two parameters—a Priority, and then an Action. I use the System.Windows.Threading.DispatcherPriority
to specify the priority of the dispatcher—Normal is fine. Then, I specify an Action by typing
New Action.
Within this delegate I’m going to specify the changes that I want to make. First, I’m
going to create an ImageSourceConverter instance, which is required to specify the source of
the updated Image control. You can’t simply provide a Source of a file path, so the ImageSourceConverter
converts from the file path to the Source in the format that it’s required.
Now that I’m within the delegate in the dispatcher, I can access updatedImage.Source,
cast it to ImageSource, and call the ImageSourceConverter.ConvertFromString method.
This is almost ready to go—I have one more change I need to make. I need to set the FileSystemWatcher.EnableRaisingEvents
to true, otherwise events won’t fire. This is ready to run now.
There’s my picture window. I’ll expand that a little bit. Now, let me pull up my
Pictures folder. You can see I have some vacation photos. I will simply copy one of these folders
to the same folder, and you can see that the WPF application detects the change, finds
the full path to the file, and displays it in the image folder. I can do this repeated
times with the same result.
Now, I want to demonstrate within the event handler how it found the folder path. Notice
that the event handler, which was automatically generated by C#, takes two parameters: the
sender object, which I don’t access (and I don’t need to access, typically) and the
FileSystemEventArgs instance. Almost all event handlers have some derivative of EventArgs,
and the default code generated by Visual Studio always calls it e. So, e has a variety of
useful parameters here, and they’re specific to the event that’s being thrown. In this
case, the FileSystemWatcher event wants to let me know the file that was changed and
what type of change happened to it. You can see that it has three properties here: ChangeType,
which specifies whether the file was changed or renamed, FullPath, which is the full path
to the file, and Name, which is just the filename. When I specified the path to the picture,
I used the FileSystemEventArgs.FullPath property, sent it to the ImageSourceConverter, and then
cast that to ImageSource.