Creating the PocketExplorer Utility

Team-Fly    

 
eMbedded Visual Basic: Windows CE and Pocket PC Mobile Applications
By Chris Tacke, Timothy Bassett
Table of Contents
Chapter 3.  Manipulating Files


The best way to familiarize yourself with the FileSystem and File controls is to dive in and begin using them for a meaningful purpose. To do that, we're going to write a small utility application called PocketExplorer that's somewhat of a mix between the standard Windows Explorer and Notepad.

PocketExplorer will allow you to navigate through all the folders on your PocketPC as well as view the files within the folders and their attributes. It will also allow you to open and edit any file, though only in text mode.

Setting Up the Project

PocketExplorer uses two formsfrmMain and frmViewand one module, modMain.

Listings 3.1 and 3.2 contain all the heading information for frmMain and frmView, respectively. For clarity, I have included listings only for properties that I modified from their defaults. Figure 3.3 shows what my project looks like.

Figure 3.3. The final Form layouts for PocketExplorer.

graphics/03fig03.gif

Listing 3.1 Heading Information from frmMain.ebf
 Begin VB.Form frmMain    Caption         =   "PocketExplorer"    ClientHeight    =   4005    ClientLeft      =   60    ClientTop       =   840    ClientWidth     =   3630    ShowOK          =   -1  'True    Begin FILECTLCtl.FileSystem fsMain       Left            =   2220      Top             =   180       _cx             =   2200       _cy             =   1400    End    Begin MSCETREEVIEWLibCtl.TreeViewCtl tvwFolders       Height          =   1755       Left            =   30       Top             =   0       Width           =   3555       LabelEdit       =   1       LineStyle       =   1       PathSeparator   =   "/"       Style           =   6    End    Begin VBCE.Label lblFiles       Height          =   255       Left            =   2160       Top             =   3720       Width           =   495       Caption         =   "Files:"       Alignment       =   1    End    Begin VBCE.CommandButton cmdDelete       Height          =   315       Left            =   1380       Top             =   3660       Width           =   615       Caption         =   "Del"    End    Begin VBCE.CommandButton cmdNew       Height          =   315       Left            =   720       Top             =   3660       Width           =   615       Caption         =   "New"    End    Begin VBCE.ComboBox cboFilter       Height          =   300       Left            =   2700       Top             =   3660       Width           =   855       Text            =   ""    End    Begin VBCE.CommandButton cmdEdit       Height          =   315       Left            =   60       Top             =   3660       Width           =   615       Caption         =   "Edit"       Enabled         =   0   'False    End    Begin VBCE.ListBox lstFiles       Height          =   1785       Left            =   30       Top             =   1800       Width           =   3555    End End 

Note

Listings 3.1 and 3.2 don't show the entire headings, but show all control settings that I've changed from their defaults.


Listing 3.2 Heading Information from frmView.ebf
 Begin VB.Form frmView    Caption         =   "FileName"    ClientHeight    =   4005    ClientLeft      =   60    ClientTop       =   345    ClientWidth     =   3630    ShowOK          =   -1  'True    Begin VBCE.CommandButton cmdSave       Height          =   375       Left            =   2400       Top             =   3600       Width           =   1035       Caption         =   "Save"    End    Begin VBCE.TextBox txtView       Height          =   3495       Left            =   30       Top             =   30       Width           =   3555       Text            =   ""       MultiLine       =   -1  'True       ScrollBars      =   3    End End 

Getting Directory Contents

One primary function of PocketExplorer is to display directory contents, so you need to write a workhorse function that can be called to do so. You want the function to read a directory, and then populate the TreeView with any subfolders in the directory as well as populate the textbox with all the directory files, provided they meet your filter conditions.

To make the code a little easier to reuse in other projects, it's a good idea to make it a function that's entirely independent of this project. That means you should have no direct references to any of the forms' controls, so what you need is a function that will take all this information as input parameters.

Call the ReadDirectory function and put it in the code module modMain.

 Public Sub ReadDirectory(Path As String, _                         fsFileSystem As FILECTLCtl.FileSystem, _                         tvwTreeView As MSCETREEVIEWLibCtl.TreeViewCtl, _                         nodParentNode As MSCETREEVIEWLibCtl.Node, _                         lstListBox As ListBox, _                         strFilter As String) 

When giving the parameter types, it's important to provide the libraries they come from (that is, FILECTLClt.FileSystem instead of just FileSystem) because without them, you won't get IntelliSense for the object inside the function.

Next , you populate the controls passed in, and the simplest way to do that is one at a time. Listing 3.3 extracts all the subdirectories from the directory passed in as the Path parameter.

Listing 3.3 Extracting All Subdirectories from a Directory
 Dim strDir As String Dim strFile As String On Error Resume Next ' Ensure path is slash terminated If Right(Path, 1) <> "\" Then Path = Path & "\" ' ----- First get all directories ----- ' Priming Read strDir = fsFileSystem.Dir(Path & "*", fsAttrDirectory) ' Iterate through directory Do While strDir <> ""     ' add directories to TreeView     tvwTreeView.Nodes.Add nodParentNode.Key, tvwChild, strDir, strDir     ' if the node already exists, ignore the error     If Err.Number = 35602 Then         ' ignore duplicate node adds - the add will fail anyway     ElseIf Err.Number <> 0 Then         MsgBox Err.Description, vbExclamation, "Error adding node!"     End If     strDir = fsFileSystem.Dir Loop 

The code in Listing 3.3 first allows for the incoming path to either be backslash-terminated or not by simply checking for it and adding it if necessary.

Next, it does a priming read with the FileSystem control's Dir() method. The first time you call Dir(), you must specify the path you want to read as well as any filtering options you want enforced. It returns a string of the first item that meets those conditions or an empty string if nothing meeting your conditions was found. In this priming read, you send in your path and "*" and specify fsAttrDirectory, which brings back all directories.

All subsequent calls to Dir() require no parameters and simply fetch the next item matching your criteria from the same directory until no more are found, at which point it returns an empty string. In Listing 3.3, this is handled with a simple Do...While loop.

If you call ReadDirectory with an already populated path, it will try to add duplicate nodes to the TreeView, which will cause an error ( specifically , error 35602Key already exists in collection).

You can handle this error by either keeping track of which directories you have already read or by checking to see if the node you are trying to add to already has children, but then you also have to determine if anything had been added, removed, or changed. You also can clear all the node's children and repopulate it, but this seems inefficient, because most often there won't be any differences. I've opted to look specifically for just error 35602 and ignore it. The error prevents duplicates and anything new will be added.

Next, you need to get all the files in the directory and populate the passed-in ListBox. Use the same logic of a priming read with the Dir() function followed by a Do...While Loop. This time in the priming read, you will send in a filter condition and specify fsAttrNormal, which will return files with normal attributes (see Listing 3.4).

Listing 3.4 Extracting a Filtered File List from a Directory
 ' ----- Next get all files ----- ' Clear ListBox lstListBox.Clear ' Priming Read strFile = fsFileSystem.Dir(Path & strFilter, fsAttrNormal) ' Iterate through directory Do While strFile <> ""     'Add Files to ListBox     lstListBox.AddItem strFile     strFile = fsFileSystem.Dir Loop 

Notice that because you clear the ListBox every time, you don't need to trap an error.

Before you can call ReadDirectory, though, you need to get all the information required by the function's parameter list. First up is the path.

The TreeView control works nicely for helping to store the current path with which the user is working. The control enables you to define a path delimiter, so by using a backslash delimiter and setting each node's text to that of a directory, you can almost get the exact string you need just by reading a node's FullPath property. The only problem is that it appends your root node's text to the beginning of the path.

To correct this, you simply need to remove the root node's text. I've done this by using the Replace function, and because you will need to get the path from multiple places in your code, I've wrapped this all in its own small function. Because the code is specific to your TreeView, I've placed the code on frmMain's code page:

 Private Function GetPath() As String     GetPath = Replace(tvwFolders.SelectedItem.FullPath, "My Device", "") End Function 

The next four parameters to ReadDirectory are simple. Just pass in references to controls on frmMain.

The final parameter is the filter, which you can use to display all files by passing in "*.*" or to limit the files displayed by passing a filter. For example, passing in "*.txt" would show only files ending in .txt.

You can get this information from the cboFilter ComboBox on frmMain.

Because the code will call ReadDirectory from multiple places, I've also wrapped a call to it in another simple function and placed it on frmMain's code page:

 Private Sub RefreshForm()     ' read the selected directory and populate the form     ReadDirectory GetPath(), fsMain, tvwFolders, _                 tvwFolders.SelectedItem, lstFiles, cboFilter.Text End Sub 

Although this isn't absolutely necessary, it's easier for coding because now you can simply call RefreshForm any time you want to read the currently selected directory.

Displaying the Directory Tree

Now that you have created functions to get the contents of any directory, let's display the directory tree in the TreeView control.

Before adding anything to the TreeView control, you must first initialize it by creating and adding the root node. Every node in the TreeView will be a child of this node. A good example of this is the My Computer root node in the standard Windows Explorer TreeView on your desktop machine.

Following this model, call your root node My Device. To add the node to the TreeView, use the Add method, passing in both the Key and Text you want to display. Leave both the Relative and Relationship parameters blank because the root has no siblings or parent. The Add method also returns a reference to the created node, which you can then use to directly call its properties and methods . In the Form_Load event for frmMain, add the following code:

 Dim nodRoot As MSCETREEVIEWLibCtl.Node ' Initialize the TreeView tvwFolders.Nodes.Clear Set nodRoot = tvwFolders.Nodes.Add(, , "Root", "My Device") 

The reason you are holding a reference to the root node isn't immediately apparent, but you will want to call a couple of methods of the root on startup, so you might as well put the code in now to hold it.

Form_Load is a good time to do any other initialization of the application as well, so put a couple of filter options into the cboFilter ComboBox and set its default as well:

 ' Initialize the Filter combobox cboFilter.AddItem "*.*" cboFilter.AddItem "*.txt" ' This will fire the ComboBox Change event, '  which will populate the first set of directories cboFilter.Text = "*.*" ' Clean up Set nodRoot = Nothing 

This adds a filter for ".txt" files and all files. Feel free to add whatever else you would like.

Setting the default filter to "*.*" causes the Change event for cboFilter to fire, just as the user changing it does. You need to add some code to be sure that these changes refilter the ListBox. Likewise, when the user taps the ComboBox to change it, you also want to handle that event. Add the code in Listing 3.5 to frmMain's code page.

Listing 3.5 Allowing Users to Filter the Files They Want to View
 Private Sub cboFilter_Change()     ' Refresh the form     RefreshForm     ' Disable the Edit button     cmdEdit.Enabled = False End Sub Private Sub cboFilter_Click()     Dim strFilter As String     ' The ComboBox Text get set AFTER the Click event,     ' so we must get it manually     strFilter = cboFilter.List(cboFilter.ListIndex)     ' Read the selected directory and populate the form     ReadDirectory GetPath(), fsMain, tvwFolders, _         tvwFolders.SelecteItem, lstFiles, strFilter     ' Disable the Edit button     cmdEdit.Enabled = False End Sub 

Note

A ComboBox's events in eVB fire in a different order than they do in VB. This is extremely important, and knowing it will save you some headaches trying to debug apparent logic error in the future. The Text property gets set after the Click event fires, so checking the Text property in the Click event handler will actually return the text that the user changed it from, not the new selection. It does, however set the ListIndex property to the new value before the event, so to get the text of the user's selection, you must get the List value at the current ListIndex.


Notice that the code in Listing 3.5 disables the Edit button in both events because the file list is getting repopulated and there will no longer be a file selected. When the user selects a file, you need to re-enable it, so add code to the ListBox's Click event:

 Private Sub lstFiles_Click()     ' If a file is selected, enable the View button     If lstFiles.ListIndex >= 0 Then         cmdEdit.Enabled = True     Else         cmdEdit.Enabled = False     End If End Sub 

Now the only thing left to do is to populate the node that the user selects by using the RefreshForm method. Handle it in the TreeView's NodeClick event like this:

 Private Sub tvwFolders_NodeClick(ByVal Index As Long)     ' Repopulate the form     RefreshForm     ' Disable the View button     cmdEdit.Enabled = False End Sub 

Finally, a nice feature would be to display all the files and subdirectories in the root directory on startup. The easiest way to do this is to manually select the root node in Form_Load, which will fire the NodeClick event. Then it's just a matter of setting the node's Expanded property to True.

The modified Form_Load event now looks like the code in Listing 3.6.

Listing 3.6 Form_Load Modified to Select and Expand the Root Node
 Private Sub Form_Load()     Dim nodRoot As MSCETREEVIEWLibCtl.Node     ' Initialize the TreeView     tvwFolders.Nodes.Clear     Set nodRoot = tvwFolders.Nodes.Add(, , "Root", "My Device")     ' Select the root node     nodRoot.Selected = True     ' Initialize the Filter combobox     cboFilter.AddItem "*.*"     cboFilter.AddItem "*.txt"     ' This will fire the ComboBox Change event,     '  which will populate the first set of directories     cboFilter.Text = "*.*"     'Expand the root node     nodRoot.Expanded = True     ' Clean up     Set nodRoot = Nothing End Sub 

Running the application at this point gives you pretty good navigation functionality. Figure 3.4 shows PocketExplorer running.

Figure 3.4. PocketExplorer's TreeView and File list in action.

graphics/03fig04.gif

Getting a File's Attributes

The next feature to add to PocketExplorer displays the attributes of a specific file when it is double-clicked in the file list. To add this functionality, use the FileSystem control's Attr(), FileLength(), and FileDateTime() methods.

First, write a function that returns a string representation of any file's attributes. Again, to make it more generic, and therefore more reusable, remove any references to the project's controls and instead pass them in. Listing 3.7 shows the full code for GetFileAttributes().

Listing 3.7 Getting a File's Attributes Given Its Path
 Public Function GetFileAttributes(Path As String, _                                     fsFileSystem As FILECTLCtl.FileSystem) _                                     As String     Dim FileAttr As FileAttrEnum     Dim lFileLength As Long     Dim dtFileDate As Date     Dim strAttributes As String     Dim strFileName As String     On Error Resume Next     ' Get the attributes.  A file can have many     FileAttr = fsFileSystem.GetAttr(Path)     ' Check if it's a system file     If FileAttr And fsAttrSystem Then         strAttributes = strAttributes & "System File" & vbCrLf     End If     If FileAttr And fsAttrReadOnly Then         strAttributes = strAttributes & "Read-Only" & vbCrLf     End If     If FileAttr And fsAttrArchive Then         strAttributes = strAttributes & "Archive File" & vbCrLf     End If     ' Get file's date     dtFileDate = fsFileSystem.FileDateTime(Path)     strAttributes = strAttributes & dtFileDate & vbCrLf     ' Get file's length     lFileLength = fsFileSystem.FileLen(Path)     ' format the size a little nicer     ' Since Format() isn't supported, we'll use integer/float division     '   as a workaround.     If lFileLength < 1024 Then         strAttributes = strAttributes & CStr(lFileLength) & " bytes"     ElseIf lFileLength < 1048576 Then         ' Go out 1 decimal place         strAttributes = strAttributes & _                CStr((lFileLength 2.4) / 10) & "k bytes"     Else         ' Go out 2 decimal places         strAttributes = strAttributes & _                CStr((lFileLength 485.76) / 100) & "M bytes"     End If     ' Return the attributes     GetFileAttributes = strAttributes End Function 

The function is pretty straightforward. First, it determines all the Attr() values for the file, such as Hidden, Read-Only, or System. It then calls FileDateTime to get the file's last modification date and FileLen to get the file's length in bytes.

Because eVB doesn't support the Format() function, and I thought it a bit ugly to show the exact number of bytes for large files, I've implemented a small workaround that formats numbers greater than 1,024 to either kilobytes or megabytes, depending on size.

Figure 3.5 shows the attributes in a MessageBox from the list's DblClick event handler (see Listing 3.8).

Figure 3.5. Displaying a file's attributes.

graphics/03fig05.gif

Listing 3.8 Displaying a File's Properties on a Double Tap
 Private Sub lstFiles_DblClick()     Dim strPath As String     Dim strAttributes As String     Dim strFileName As String     ' Make sure the user double-clicked a valid item     If lstFiles.ListIndex < 0 Then Exit Sub     ' Get the filename     strFileName = lstFiles.List(lstFiles.ListIndex)     ' Get the file's path     strPath = GetPath() & "\" & strFileName     ' Show the file's attributes     strAttributes = GetFileAttributes(strPath, fsMain)     ' Display the info to the user     MsgBox strAttributes, vbInformation + vbOKOnly, strFileName End Sub 

Opening, Editing, and Writing Text Files

The next features you will add to PocketExplorer are similar to those you get on a desktop PC through Notepadthe capability to create, open, and edit text files. The functions you need to implement these features are found in the File control.

Creating the File Object Without Leaking Memory

This is a good opportunity to demonstrate a few of eVB's features. Rather than drop a File control onto frmMain, you can use CreateObject().

As I mentioned in Chapter 2, CreateObject also creates a memory leak. Every time you call it, you lose a chunk of memory, and if you call it often enough, you probably force users to reset their devices.

One way to avoid multiple calls to CreateObject is simply to call it the first time you need the object, and then keep the object around for the life of your application. Rather than create a new object every time you need one, just reuse the original. This increases the overall memory your application requires because you can never release the object, but minimizing the impact of CreateObject's memory leak is more important.

Note

This is a trade-off you need to think about for any project that uses CreateObject. If the object is large and rarely used, you may want to destroy and re-create it only when used to conserve memory. Be aware, however, that each time you call CreateObject, you'll lose memory until your application is fully shut down.


Another frustrating aspect of using CreateObject in eVB is that the library and class names exposed to the Object Browser and IntelliSense aren't always the same as the names used in the object's type library. The File control falls into this category. If we look in the Object Browser or use IntelliSense, it seems that we would want to create a FILECTLCtl.File object, but using this as a parameter to CreateObject will result in error 429, ActiveX Component can't create object. This is because the library name isn't actually FILECTLCtl, but just FILECTL. (Notice the omission of the last three letters , Ctl.)

Tip

As a rule of thumb, if you get an error 429 when trying to create an object that ends in Ctl, Lib, or some combination of them, try creating the object without those letters:

 CreateObject("FILECTL.File") 

Another option for finding the name is to open the library's typelibrary or typelibrary cache. These are usually files with the same name as the DLL, but with a .tlb or .oca extension. In this case, it's MSCEFILE.oca and it can be found in the \Program Files\Microsoft eMbedded Tools\EVB\devctrls directory of your development PC. Opening the file with Notepad displays largely unprintable garbage, but all the library and class names should be readable. Be careful not to modify the file's contents.

Caution

.oca and .tlb files are binary files used by the system. Modifying them in any way can render their associated controls unusable.


With all that said, let's create a global File object variable and a function that both creates it and returns a reference to it. This way you can just call this function whenever you need your File control and let the function handle whether it actually needs to call CreateObject(). First, in the General Declarations section of modMain, add the variable declaration:

 Private m_FileObject As FILECTLCtl.File 

And then add the function shown in Listing 3.9.

Listing 3.9 Minimizing the Impact of the CreateObject Memory Leak
 Public Function GetFileObject() As FILECTLCtl.File     On Error Resume Next     ' If we haven't created the File object yet, do so     If IsEmpty(m_FileObject) Then         ' Create a File control         Set m_FileObject = CreateObject("FILECTL.File")         ' Ensure the object was created successfully         If Err.Number <> 0 Then             MsgBox "Failed to create File object." & vbCrLf & _                 "Ensure MSCEFile.dll has been installed and registered.", _                 vbCritical, "Error"             Exit Function         End If     End If     ' Return our global File object     Set GetFileObject = m_FileObject End Function 
Deleting a File

Deleting a file is probably the simplest file function, so it is covered first. When the user selects a file in PocketExplorer and taps the Delete button, you call the Kill function of your FileSystem object. As a courtesy , it's always a good idea to give a warning message to ensure that the user really wants to delete the file. Listing 3.10 shows the event handler for the Delete button, and Figure 3.6 shows the Delete dialog.

Figure 3.6. Asking before you delete a file is a general courtesy.

graphics/03fig06.gif

Listing 3.10 Event Handler for the Delete Button in frmMain.cbf
 Private Sub cmdDelete_Click()     Dim strPath As String     Dim strFileName As String     On Error Resume Next     ' Make sure a file is selected     If lstFiles.ListIndex < 0 Then Exit Sub     ' Get the path to the file     strFileName = lstFiles.List(lstFiles.ListIndex)     strPath = GetPath() & "\" & strFileName     ' Make sure the user wants to delete the file     If MsgBox("Delete file?: " & vbCrLf & strFileName, _             vbYesNo, "Delete File?") = vbNo Then Exit Sub     ' Delete the file     fsMain.Kill strPath     ' Check for success     If Err.Number <> 0 Then         MsgBox "Unable to delete file.  It may be in use or protected", _                 vbExclamation, "Error"     End If     ' Repopulate the form     RefreshForm End Sub 
Reading a File's Contents

Reading the contents of a sequential text file is a straightforward operation similar to using the fso in Win32. Essentially, you open the file, loop through the contents until you hit EOF, and then close the file. We'll use the GetFileTextContents function in Listing 3.11 to return the contents of any text file as a String.

Listing 3.11 Extracting the Contents of a Text File as a String
 Public Function GetFileTextContents(Path As String) As String     Dim filFile As FILECTLCtl.File     Dim strInput As String     On Error Resume Next     ' Get our application File object     Set filFile = GetFileObject()     ' Open the File     filFile.Open Path, fsModeInput, fsAccessRead     ' Make sure the call to Open was successful     If Err.Number <> 0 Then         MsgBox "Open Method failed." & vbCrLf & _             "The file could not be read.", _             vbCritical, "Error"     End If     ' Loop through file, filling our input buffer     Do While Not filFile.EOF         strInput = strInput & filFile.Input(1)     Loop     ' Close the file     filFile.Close     ' Release the File Object     Set filFile = Nothing     ' Return the text     GetFileTextContents = strInput End Function 

When the user taps the Edit button, use the function to read the file's contents and fill the multiline TextBox on frmView (see Figure 3.7).

Figure 3.7. Editing a file with PocketExplorer.

graphics/03fig07.gif

Just like Notepad, you don't hold the file open while it's being viewed , so you need to store the filename somewhere so you know where to save any changes the user makes. A simple way would be to just set a global variable in either frmView or modMain, but let's take this opportunity to implement a workaround to the fact that eVB doesn't support user-defined properties.

Just like a property, you need to have a member variable in which to keep the value and implement both a Set and Get method to set or get that member variable's value. In frmView's General Declarations section, add the following member variable:

 Private m_strPath As String 

And then add the following code:

 ' Since Properties aren't supported, we'll use a Set/Get '   method pair as a workaround Public Sub SetPath(NewPath As String)     ' Store the full path to the current file     m_strPath = NewPath End Sub Public Function GetPath() As String     ' Retrieve the full path to the current file     GetPath = m_strPath End Function 

You don't get a single name like you would for an actual property, but these two functions otherwise perform just like a property.

As another courtesy to users, keep track of whether they've saved their changes to the file so you can prompt them before they exit. To do this, keep a dirty flag in frmView, turning it off when they save and on when they make changes. When you first load the file, you need to clear the flag. Add the following to the General Declarations section of frmView:

 Private m_bDirty As Boolean 

And then add a function to clear the flag:

 Public Sub ClearDirty()     ' We must clear this flag every time we show the form     m_bDirty = False End Sub 

While you're at it, you might as well add code that turns the flag back on. It's safe to assume that any stylus tap in the txtView TextBox will be a change to the file, so we'll turn the flag on in that event handler like so:

 Private Sub txtView_KeyPress(ByVal KeyAscii As Integer)     m_bDirty = True End Sub 

Now let's look at the implementation of the event handler for the Edit button (see Listing 3.12). Get the path to the selected file, set your new property to that value, get the file's contents, fill frmView's TextBox, clear the dirty flag, and then lastly, hide frmMain.

Listing 3.12 Opening and Displaying a Text File for User Modification
 Private Sub cmdEdit_Click()     Dim strPath As String     Dim strContents As String     Dim strFileName As String     ' Build the path to the file     strPath = GetPath()     strFileName = lstFiles.List(lstFiles.ListIndex)     strPath = strPath & "\" & strFileName     ' Set frmView's Path "property"     frmView.SetPath strPath     ' Get the file contents as text     strContents = GetFileTextContents(strPath)     ' Set frmView's caption     frmView.Caption = strFileName     ' Show frmView     frmView.Show     ' Populate the View textbox     frmView.txtView.Text = strContents     ' Set the form's dirty flag     frmView.ClearDirty     ' Hide frmMain     frmMain.Hide End Sub 
Creating a New File

All that's left to do now is to add the capability to create a new file and save the changes the user makes to existing files. Both tasks can be handled with a single function.

Rather than determine what changes a user actually made to a file, it's much simpler to just overwrite the entire file. What you need then is a function that accepts the path to your file and your file's text as parameters (a new file will just have no text passed in). The function then needs to either create the file if it doesn't exist or overwrite it if it does, and write the passed-in text to it.

Again, you can write a generic function that can be reused in other projects (see Listing 3.13). A generic name such as CreateFile also facilitates reuse.

Listing 3.13 Creating and Populating a Text File
 Public Sub CreateFile(Path As String, Contents As String)     ' This function performs double duty. We use it to create     '   new files as well as overwrite old files     Dim filFile As FILECTLCtl.File     On Error Resume Next     ' Get our application File object     Set filFile = GetFileObject()     ' Open the file, if it doesn't exist, it will be created     ' fsModeOutput means overwrite     filFile.Open Path, fsModeOutput, fsAccessWrite, fsLockWrite     ' Make sure the call to Open was successful     If Err.Number <> 0 Then         MsgBox "Open Method failed." & vbCrLf & _             "The file could not be created.", _             vbCritical, "Error"     End If     ' Write our data to the file if it's not empty     If Contents <> "" Then         filFile.LinePrint Contents     End If     ' Close the file     filFile.Close     ' Release the File Object     Set filFile = Nothing End Sub 

Now you need to add a call to CreateFile from frmMain's New button. Prompt for a new filename by using the InputBox function. If the user doesn't enter a name or taps Cancel, InputBox returns an empty string.

Also add a default .txt extension to the filename if the user didn't provide one (see Listing 3.14).

Listing 3.14 Getting a Filename from the User and Checking for an Extension
 Private Sub cmdNew_Click()     Dim strFileName As String     Dim strPath As String     ' Get a filename from the user - omit white spaces     strFileName = Trim(InputBox("Filename to create:", "New File"))     ' If the return was blank, exit     If strFileName = "" Then Exit Sub     ' Check for an extension by checking for a period     If InStr(strFileName, ".") = 0 Then         ' If an extension wasn't provided, default to .txt         strFileName = strFileName & ".txt"     End If     ' Get the currentpath and append our filename     strPath = GetPath() & "\" & strFileName     ' Create the file     CreateFile strPath, ""     ' Repopulate the form     RefreshForm End Sub 

Finally, you need to add a call from frmView's Save button:

 Private Sub cmdSave_Click()     ' Save the file     CreateFile GetPath(), txtView.Text     ' Clear the dirty flag     m_bDirty = False End Sub 
Adding the Final Touches

That pretty much completes PocketExplorer. You do need to add a few finishing touches to make it a bit more user friendly, though.

The PocketPC standard for exiting most forms is to tap the OK button in the upper-right corner of the form's title bar. The OK button has its own event handler to make this simple.

When users finish editing a file and tap OK, you should check to see whether they've saved all their changes and allow them to do so if they haven't. Do this with a simple MsgBox call (see Figure 3.8).

Figure 3.8. The Save Changes dialog.

graphics/03fig08.gif

Listing 3.15 shows frmView's OKClick event handler.

Listing 3.15 Asking to Save Changes Before Closing the Application
 Private Sub Form_OKClick()     ' check to see if there are unsaved changes     If m_bDirty Then         ' Prompt to save changes         If MsgBox("Save changes before exit?", vbYesNo, "Save?") = vbYes Then             ' Save changes             CreateFile GetPath(), txtView.Text         End If     End If     frmMain.Show     frmView.Hide End Sub 

When the user exits the application by tapping OK on frmMain, you want to be sure to clean up after yourself, releasing any objects created. In the case of PocketExplorer, you should release the File control you created. Because the File control is held privately in modMain, you will need an access method to release it:

 Public Sub ReleaseFileObject()     Set m_FileObject = Nothing End Sub 

And finally, call your release method from frmMain's OKClick event handler and end the application:

 Private Sub Form_OKClick()     ' Clean up our File object     ReleaseFileObject     ' End app     App.End End Sub 

Team-Fly    
Top
 


eMbedded Visual BasicR. WindowsR CE and Pocket PC Mobile Applications
eMbedded Visual BasicR. WindowsR CE and Pocket PC Mobile Applications
ISBN: N/A
EAN: N/A
Year: 2001
Pages: 108

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net