Z Code

Build a Simple File Watch App

Learn new features to jazz up your WinForms apps with this month's handy FileWatch utility. FileWatch monitors files and directories for changes, and runs as a task tray icon.

P>Technology Toolbox: VB.NET

A file watch program monitors a file, a set of files matching a pattern, or a whole directory structure for file changes. It lives unobtrusively in the task tray and notifies you of changes by altering its tray icon visually and/or with audio feedback.

Creating one isn't hard. I created a simple file-monitoring utility called FileWatch that displays a small icon in the task tray area of the taskbar. FileWatch also takes advantage of several useful but lesser-known .NET components, including ToolTip, FileSystemWatcher, NotifyIcon, and ContextMenu. Download the sample app and follow along.

A nice bonus: FileWatch illustrates many of the benefits of using WinForms over Web Forms. WinForms apps, often known as rich-client apps, have several advantages over their Web-based cousins: a wider selection of form controls to choose from, the ability to "connect" data between related form controls more easily, and easier custom input validation. A WinForms app also generally has more privileges to read/write files and access other resources on the local machine than a Web Form app.

Begin by firing up VS.NET, creating a new project using the Windows Application template, and naming the project FileWatch. Change the default form filename from Form1.vb to frmWatch.vb, set the form name to frmWatch, and set the form's Text property to FileWatch Utility. Set the frmWatch Icon property to the FileWatch.ico file included in the sample download. Next, right-click on FileWatch in the Solution Explorer and choose Properties from the popup menu. Choose frmWatch as the startup object and click on OK.

From the toolbox, drop a GroupBox control on the form (Name = grpMonitor; Text is a descriptive string). Drop controls on the GroupBox: a TextBox (Name = txtWatchPath; Text cleared), a CheckBox (Name = chkMonitorSubs; Text = Monitor Subdirectories), and a Button (Nam = btnBrowse; Text = Browse). The GroupBox is useful for enabling or disabling a group of controls at the same time.

Select the form itself and drop these controls on the form: a CheckBox (Name = chkEnable; Text =Enable Monitoring), a Button (Name = btnOK; Text = OK), and a StatusBar (Text set to some descriptive instructions). Set the StatusBar SizingGrip property to False, and set the FormBorderStyle property to Fixed3D to disallow form resizing at run time (see Figure 1).

Next, drop an OpenFileDialog (OFD) control on the form. OFD doesn't have a visible user interface at design time, so it appears in the component tray area below the form. Double-click on the Browse button on the form to jump to its code-behind Click event handler, then add code to interpret what's entered in txtWatchPath and control file browsing (see the sample app). You'll probably need to add this Imports statement to the top of the source file to provide namespace definition for the File and Directory objects:

Imports System.IO

OFD allows you to select a specific filename to monitor. You must type directory or filespec patterns, such as d:\my documents\*.txt, into the TextBox manually. You can also type a UNC path, such as \\server\share\subdir\file.ext, to monitor files on a different machine in your network.

Enable Drag-and-Drop
Add drag-and-drop (DnD) functionality to the TextBox to allow filename dragging from Windows Explorer and other sources. DnD works differently in .NET than it did in VB6, but it's easy to figure out how to use it. Set txtWatchPath's AllowDrop property to True to enable DnD to the control. .NET DnD includes four events available for controls that support it: DragEnter, DragOver, DragLeave, and DragDrop. DragEnter sets the mouse cursor to the appropriate icon:

Private Sub txtWatchPath_DragEnter(?)
	e.Effect = DragDropEffects.All
End Sub

DragOver and DragLeave fire when an object is dragged over the control, or the dragged object leaves the control. The DragDrop event fires when an object is dropped on the control. You determine what data was dropped with the DragEventArgs' (the variable named e) GetDataPresent method, and you retrieve data with its GetData method:

Private Sub txtWatchPath_DragDrop(?)
	If e.Data.GetDataPresent _
		(DataFormats.FileDrop) Then
		Dim sFiles() As String = _
			e.Data.GetData _
			(DataFormats.FileDrop)
		txtWatchPath.Text = sFiles(0)	
	End If
End Sub

Files from Windows Explorer arrive in the FileDrop data format. Handle multiple dropped files by taking the first file in the list (see Additional Resources for more on DnD).

You might have a long path displayed in the TextBox, so drop a ToolTip control on the form. Set txtWatchPath's "ToolTip on ToolTip1" property to a default string, and set the displayed path to the ToolTip text in the TextBox's TextChanged event:

ToolTip1.SetToolTip _
	(txtWatchPath, txtWatchPath.Text)

You also can provide user guidance for all controls on the form by setting default values on each to ToolTip1 (see the sample app for examples).

Drop a FileSystemWatcher (FSW) component on the form from the Components tab of the toolbox. Set the File and Path properties according to the TextBox contents, and set the NotifyFilter property to any combination of NotifyFilters enumeration values (see Additional Resources). The FileSystemWatcher component raises events based on the NotifyFilters criteria you set. FSW raises four types of events: Changed, Created, Deleted, and Renamed. Create two event routines to handle these:

Private Sub fsw_Events(?) Handles _
	FileSystemWatcher1.Changed, _
	FileSystemWatcher1.Created, _
	FileSystemWatcher1.Deleted
	Dim sChange As String
	Select Case e.ChangeType
		Case WatcherChangeTypes.Changed
		sChange = "File Changed: " & _
			e.FullPath
		'other cases here
	End Select
	Beep()	'take changed action
	StatusBar1.Text = sChange
End Sub

You must create a separate event handler for the Renamed event because its second parameter is different. The Change action in the sample app calls the Beep function and sets the StatusBar text—you can also use a popup message or other appropriate action. Start or stop monitoring files with FSW when you click on the Enable Monitoring checkbox:

Private Sub _
	chkEnableMonitor_CheckedChanged(?)
	If CType(sender, CheckBox).Checked _
		Then
		StartMonitor()
	Else
		StopMonitor()
	End If
End Sub

The StartMonitor routine sets up the FSW control according to your chosen settings and starts file monitoring (see Listing 1).

Minimize to the Task Tray
Drop a NotifyIcon control on the form to enable it to minimize to the task tray. The .NET Framework makes this easy to implement compared to the ordeal you faced to implement a tray icon in VB6. Set frmWatch's ShowInTaskbar property to False so it doesn't show the normal large taskbar icon when minimized. Double-click on the OK button and add code to minimize the form when you click on it:

Me.WindowState = _
	FormWindowState.Minimized

You switch between a set of three task tray icons to indicate the state of the app: white when idle, green when monitoring, and red when a file change is detected. Drop an ImageList control on the form to contain the icons. Click on the Browse button in the Images property of the ImageList control. Click on the Add button and browse to the nomon.ico icon in the sample code. Also, add the monitor.ico and changed.ico icons to the ImageList control, in that order.

Add an Enum to the top of the class file that defines four monitoring states, including a startup mode:

Private Enum MonitorState
	msStartup
	msNotMonitoring
	msMonitoring
	msFileChanged
End Enum

Next, create a routine named SetNotifyIcon. This routine takes MonitorState and string parameters, and sets the NotifyIcon's Text and Icon properties (see Listing 2). Include calls to SetNotifyIcon wherever you detect changes to the state of the application: in frmWatch_Load, in fsw_Events and fsw_Renamed event routines, and in the StartMonitor and StopMonitor methods.

Drop a ContextMenu control on the form and set NotifyIcon1's Context Menu property to ContextMenu1 so you can control the app from its tray icon. Click on the ContextMenu1 item in the component tray below the form to show its designer in the menu bar area of the form. Click on the Context menu item and type in five items where the designer prompts "Type Here": &Monitor, &Reset, &Show Window, a hyphen to add a spacer bar, and E&xit. Each ampersand (&) tells the menu control to make the following character an Alt-key shortcut for the menu command.

Only a handful of steps remain. Right-click on the menu designer and choose Edit Names from the popup menu. Give each text menu item a name: ctxMonitor, ctxReset, ctxShowWindow, and ctxExit. Double-click on each menu item to create event routines in the code-behind file, then create another routine to toggle monitoring and set a check mark in front of the ctxMonitor item:

Private Sub ToggleMonitor()
	If ctxMonitor.Checked Then
		ctxMonitor.Checked = False
		StopMonitor()
	Else
		ctxMonitor.Checked = True
		StartMonitor()
	End If
End Sub

Call the ToggleMonitor routine from the ctxMonitor_Click event routine, and add code to the other menu events appropriate to the action for each (see the sample code).

Finally, call the ToggleMonitor routine from the NotifyIcon1_DoubleClick event routine. This lets you enable and disable monitoring simply by double-clicking on the tray icon. In StartMonitor and StopMonitor, set the checked state of both chkEnable and ctxMonitor to keep the form's chkEnable control in sync with the ctxMonitor menu item.

WinForms apps are not dying out in preference to Web-based apps, contrary to many published reports. WinForms apps are convenient, especially for utilities, and are still much easier to build than Web apps. There are many controls available to make your coding job simpler, including some that programmers seldom take notice of. The FileWatch utility includes several of these interesting controls. Your next step is to continue exploring the toolbox and see what else you can uncover to give your apps that professional edge.

comments powered by Disqus
Most   Popular