Accessing the file system via the FolderItem class

All file access use done using a class called FolderItem. A FolderItem is anything that can be stored on a drive such as volumes, folders, files, applications, and documents.

Using the FolderItem class, you can get a reference to any such items on your drives. To read from a file, you need a FolderItem for it. To write to a file, you need a FolderItem. When you ask users to select a file using one of the file selectors, you get a FolderItem referring to the file they selected.

Once you have a FolderItem, you can refer to its properties (such as Name or path) and perform actions on it such as deleting or copying it.

Shortcuts and aliases

Shortcuts (aliases on Mac) are files that actually represent a volume, app, folder, or file stored in another location and possibly under another name. The FolderItem class contains properties and methods that allow you to either resolve the shortcut and work with the actual object or work with the object directly. The FolderItem constructor, Child and ChildAt methods automatically resolves a shortcut when it encounters it. However, each also provides an option to work with the shortcut itself.

File locations

When you create your own files, you should avoid creating them beside the app itself. Modern operating systems restrict permissions on apps in the Application (or Program Files) folders so you would get an error in most cases. In addition, when running your app from Xojo on Windows, the build folder is recreated each time you run so any files you place alongside the app will be deleted.

You should instead use more appropriate locations for files such as the user's Documents folder or system folders such as Application Support or AppData. You can get access to these locations using SpecialFolder.Documents or SpecialFolder.ApplicationData. Refer to the next section for more information.

Accessing a file from a specific location

If you know the full path to a file and you wish to access the file, you can do so by specifying the path to the file.

For example, suppose you have a document called “Schedule” stored in the same folder as your app. The relative path starts with the folder your app is in. The FolderItem.Constructor function can be used to quickly get a reference to a file as seen in the following code:

Var f As FolderItem
f = New FolderItem("Schedule")

To get the folder where your app resides: The full path (sometimes called the native path) to a volume, folder, app, or document starts with the volume name followed by the path delimiter character (a backslash on Windows and a forward slash on Mac and Linux), the names of any folders in the path (each separated by the path delimiter) and ending with the name of the item.

To create a native path to a file or folder, you should use the FolderItem.DriveAt shared method to build a full path to the item, starting with the drive it is on. You then use the Child method of the FolderItem class to navigate to the item. DriveAt returns a FolderItem for one of your mounted volumes. You specify the volume by passing an integer, indicating the volume. Volume 0 is the volume that contains the operating system — the “boot” volume.

Var f As FolderItem
f = FolderItem.DriveAt(0) // the boot volume

The Parent property returns the FolderItem for the next item up in the path for the current FolderItem. It returns Nil if you try to get the parent of a volume. The Child and ChildAt methods let you access any items one level below the current FolderItem.

You can build a full path starting from a volume with the Child method. For example, if you want to get a FolderItem for the file “Schedule” in the folder “Stuff” on the boot volume, the code would be:

Var f As FolderItem
f = FolderItem.DriveAt(0).Child("Stuff").Child("Schedule")

The following code works with a relative path. It uses the Parent property to get the FolderItem for the folder that contains the folder in which the app is located. Passing the empty string to the FolderItem.Constructor gets the current folder, so the parent of that folder is one level up in the hierarchy.

Var f As Folderitem
f = FolderItem("").Parent

Once you have a FolderItem, you can (depending on what type of item it is) copy it, delete it, rename it, read from it or write to it, etc. You will learn how to read and write to files using FolderItems later in this topic.

The FolderItem.Constructor method has an optional parameter that allows you to pass a native path, a shell path, or a URL path. It uses the PathModes enumeration from the FolderItem class.

You specify the type of path by passing one of the class constants as the second parameter in a call to FolderItem.Constructor. For example, the following uses a shell path on Linux. It returns a FolderItem for the “Documents” folder in the home folder for the user “Joe.”

Var f As FolderItem
f = New FolderItem("/home/Joe/Documents", FolderItem.PathModes.Shell)
If f.Exists Then
  TextField1.Text = f.NativePath
Else
  MessageBox("The FolderItem does not exist.")
End If

A URL path must begin with “file:///” The following example uses the URL path to the user's “Documents” folder on Windows:

Var f As FolderItem
f = New FolderItem("file:///C:/Documents%20and%20Settings/" _
  + "Joe%20User/My%20Documents/", FolderItem.PathModes.URL)
If f.Exists Then
  MessageBox(f.NativePath)
Else
  MessageBox("The FolderItem does not exist.")
End If

The FolderItem class's properties NativePath, URLPath, and ShellPath contain the types of paths.

Accessing system folders

Operating systems have specific locations for various folders that contain information, such as the Documents folder for the user.

Use the SpecialFolder module to get FolderItems representing these special system folders. The benefit of using this module rather than attempting to recreate the path manually, is that SpecialFolder always works and is correct across platforms (in most cases) and languages.

You obtain the desired FolderItem using the syntax:

result = SpecialFolder.FolderName

where result is the FolderItem you want to obtain and FolderName is the name of the SpecialFolder function that returns that FolderItem. For example, the following gets a FolderItem for the "Application Support" folder on macOS and the "Application Data" directory on Windows:

Var f As FolderItem
f = SpecialFolder.ApplicationData

Refer to SpecialFolder for the complete list of supported functions and the FolderItems that are available for macOS, Windows and Linux.

Note that not all functions return FolderItems on all platforms. If a FolderItem is not defined on all platforms, you should use an alternative function that returns a FolderItem on every platform. Check that the result is not Nil before using the FolderItem. For example, SpecialFolder.Documents returns the current user's Documents folder on macOS and Windows but returns Nil on Linux. On Linux, you should call SpecialFolder.Home instead. For example:

Var f As FolderItem
#If Not TargetLinux
  f = SpecialFolder.Documents
#Else
  f = SpecialFolder.Home
#EndIf
If f <> Nil Then
  If f.Exists Then
    // use the FolderItem
  End If
Else
  MessageBox("FolderItem is Nil!")
End If

Verifying the FolderItem

When you try to get a FolderItem, either of two things can go wrong. First, the path may be invalid. An invalid path contains a volume reference and/or a folder name that doesn 't even exist. For example, if you use FolderItem.DriveAt and pass it 3 when the user has only one drive, the FolderItem.DriveAt function returns a Nil value in the FolderItem instance, f. If you try to use any of the FolderItem class's properties or methods on a Nil FolderItem, a NilObjectException error will occur. If the exception is not handled in some way, the app will quit.

Second, the path may be valid, but the file you are trying to access may not exist. The following shell code checks for these two situations:

Var f As FolderItem
f = SpecialFolder.Documents.Child("Schedule")
If f <> Nil Then
  If f.Exists Then
    MessageBox(f.NativePath)
  Else
    MessageBox("File does not exist!")
  End If
Else
  MessageBox("Invalid path!")
End If

If the path is valid, the code checks the Exists property of the FolderItem to be sure that the file already exists; if the file doesn 't exist or the path is invalid, a warning message is displayed.

You can also handle an invalid path using an Exception Block. They are discussed in the Exception Handling section in the Debugging chapter.

Creating new FolderItems

You can create a FolderItem for an existing item by passing it the pathname. When you create a FolderItem with the New command, you can pass the path to the new FolderItem as an optional parameter. For example:

Var f As FolderItem
f = New FolderItem("myDoc.txt")

specifies the name of the new FolderItem and it is located in the same folder as your application (if you 're running in the IDE) or the same folder as the built app.

If you pass a FolderItem instead of a path, New will create a copy of the passed FolderItem. In this example, the FolderItem “f2” refers to a copy of the original FolderItem, not a reference to it.

Var f, f2 As FolderItem
f = SpecialFolder.Documents.Child("Schedule")
If f <> Nil Then
  f2 = New FolderItem(f)
End If

Creating files on web servers

If your web app creates files (or folders) on web servers, including Xojo Cloud, you have to ensure you set the appropriate permissions to that you can later write to (or delete) them.

To do so, use the Permissions property of the FolderItem class. By default, files created on the web server get the permissions of the parent folder. This setting gives a file read/write access for all users:

Var f As New FolderItem("myFile.txt")
f.Permissions = 438 'Octal 666

Refer to the FolderItem.Permissions property for specifics on how you can set permissions.

Deleting FolderItems

Once you have a FolderItem that represents an item that can be deleted, you can call the Delete method. The following example deletes the file represented by the FolderItem:

myFile.Remove

If the FolderItem is locked, an error will occur. You can check to see if the FolderItem is locked by checking the FolderItem's Locked property. Deleting a FolderItem does not move the FolderItem to the trash -- it is deleted permanently from the volume.

Var f As FolderItem
f = SpecialFolder.Documents.Child("Schedule")
If f <> Nil Then
  If f.Exists Then
    f.Remove
  End If
End If

Finding the default folder

Passing an empty string (two quotes with no characters in between them) to the FolderItem.Constructor function returns a FolderItem representing the folder your app is in. You can then use the FolderItem's Item method to access all the items in the folder your app is in. The Item method returns an array of FolderItems in the folder. The array is one-based. You get a FolderItem for an item by passing the Item method the index of the item.

For example, the following method gets a FolderItem for the folder and populates a ListBox with the paths to each item in the Folder. It uses an iterator to loop through the FolderItems and the Child method to get a FolderItem for each item.

Var f As New FolderItem("")
For Each file As Folderitem In f.Children
  If file <> Nil Then
    ListBox1.AddRow(file.NativePath)
  End If
Next

The following code returns a FolderItem that represents a file called “My Template” in a folder called “Templates” that is located in the same folder as the app:

Var f As FolderItem
f = FolderItem("Templates").Child("My Template")

Iterating through folder contents

You may find that you need to iterate through a folderitem (that is a folder) in order to process all the files in it. Here is an example that deletes all the files in a folder:

// TargetFolder is a FolderItem that points to a folder.

Var itemsToRemove() As FolderItem
Var n As Integer = TargetFolder.Count

If n > 0 Then
  For i As Integer = 1 To n
    For Each file As FolderItem In TargetFolder.Children
      If file.Exists And Not file.IsFolder Then
        itemsToRemove.AddRow(file)
      End If
    Next
  Next
End If

For i As Integer = 0 To itemsToRemove.LastIndex
  itemsToRemove(i).Remove
Next

This code saves the files that are to be deleted in an array so that they can be deleted after all the files have been processed in order to improve performance. A more complete example is available in the FolderItem.Remove section of the FolderItem page.

Prompting the user for files and folders

There are several methods available to allow the user to select files while using your apps. You may want to allow your user to specify a file to open, to specify the name of a file to save or you may want to let them choose a folder.

These commands can only be used in desktop apps.

Opening files

The simplest method for prompting the user to select a file to open is to use the FolderItem.ShowOpenFileDialog function as follows:

Var f As FolderItem
f = FolderItem.ShowOpenFileDialog("")
MessageBox(f.ModificationDateTime.ToString)

The FolderItem.ShowOpenFileDialog function displays the Open File selector and returns a FolderItem object that represents the file the user selected. One or more file types (that have been defined in the File Type Group Editor or with the FileType class via the language.) must be passed to the FolderItem.ShowOpenFileDialog function. It presents only those file types to the user in its browser. In this way, the user can only open files of the appropriate type. To pass more than one file type, separate them with semicolons.

If the user clicks the Cancel button rather than the Open button in the Open File selector, FolderItem.ShowOpenFileDialog returns Nil. You will need to make sure the value returned is not Nil before using it. If you don 't, your app will crash with a NilObjectException. The following code shows how the code from the previous example should be written to check for a Nil object:

Var f As FolderItem
f = FolderItem.ShowOpenFileDialog(FileTypes1.jpeg)
If f <> Nil Then
  MessageBox(f.ModificationDateTime.ToString)
End If

For more precise control, you can use the OpenFileDialog class to create an Open File selector. The class allows you to create a customizable open-file dialog box in which you can specify the following aspects of the dialog:

  • Position (Left and Top properties)

  • Default folder (InitialFolder property)

  • Valid file types to show (Filter property)

  • Text of Validate and Cancel buttons (ActionButtonCaption and CancelButtonCaption properties)

  • Text that appears above the file browser (Title property)

  • Text that appears below the file browser (PromptText property)

When you use the OpenFileDialog class, you create a new object based on this class and assign values to its properties to customize its appearance. The following example uses a custom prompt and displays only one file type:

Var dlg As New OpenFileDialog
dlg.InitialFolder = SpecialFolder.Documents

dlg.Title = "Select a MIF file"
dlg.Filter = FileTypes1.pdf

Var f As FolderItem
f = dlg.ShowModal
If f <> Nil Then
  // Proceed normally
Else
  // User Cancelled
End If

Saving files

The Save As selector is used to let the user choose a location in which to save a file and give the file to be saved a name.

The FolderItem.ShowSaveFileDialog function presents the Save As dialog box. The SaveFileDialog class allows you to create a customized version of this dialog. Both objects return a FolderItem that represents the file the user wishes to save. This is an important distinction because the file doesn 't exist yet. You must provide additional code that will create the file and write the data to the file. You will learn about creating files and writing data later in the Accessing Text Files and Accessing Binary Files topics.

When you call the FolderItem.ShowSaveFileDialog function, you define the type of file and the default name for the file (that appears in the Name field in the Save As selector). The file type (which is the first parameter of the function) is the name of any file type defined for the project in the File Types dialog box. Like the other functions that return FolderItems, you should make sure the FolderItem returned by FolderItem.ShowSaveFileDialog is not Nil before using it (which can happen if the user clicked Cancel).

This code displays a Save As selector with a default filename of “Untitled”:

Var f As FolderItem
f = FolderItem.ShowSaveFileDialog(FileTypes1.jpeg, "Untitled")
If f <> Nil And f.Exists Then
  MessageBox(f.Name)
End If

When you use the SaveFileDialog class, you create a new object based on this class and customize the dialog by assigning values to its properties. You can customize the following aspects of the dialog:

  • Position (Left and Top properties)

  • Default directory (InitialFolder property)

  • Valid file types to show (Filter property)

  • Default filename (SuggestedFileName property)

  • Text of the Validate and Cancel buttons (ActionButtonCaption and CancelButtonCaption properties)

  • Text that appears above the file browser (Title property)

  • Text that appears below the file browser (PromptText property)

The following code opens a customized save-file dialog box and displays the contents of the Documents directory in the browser area.

Var dlg As New SaveFileDialog
dlg.InitialFolder = SpecialFolder.Documents
dlg.Title = "Select a pdf file"
dlg.Filter = FileTypes1.Pdf

Var f As FolderItem
f = dlg.ShowModal
If f <> Nil Then
  // Proceed normally
Else
  // User Cancelled
End If

Selecting folders

Sometimes you need to have the user select a Folder rather than a file using the Folder selector. You can do this using the FolderItem.ShowSelectFolderDialog function:

Var f As FolderItem
f = FolderItem.ShowSelectFolderDialog
If f <> Nil Then
  MessageBox(f.Name)
End If

If you need more control over this selector, you can use the SelectFolderDialog class instead. The class has properties to modify the:

  • Action button caption

  • Cancel button caption

  • Initial Folder

  • Position on screen

  • Prompt text

  • Title

  • Default folder name

This code displays a customized Folder selector:

Var dlg As New SelectFolderDialog
dlg.ActionButtonCaption = "Select"
dlg.Title = "Title Property"
dlg.PromptText = "Prompt Text"
dlg.InitialFolder = SpecialFolder.Documents

Var f As FolderItem
f = dlg.ShowModal
If f <> Nil Then
  // Use the FolderItem here
Else
  // User cancelled
End If

File formats

When saving your own files, you have to decide on the file format. You can use a text-based format such as JSON or XML or choose a binary format where you can include whatever types of data you want.

If you need to save a combination of data such as both text and graphics, here are some other options:

  • You could use a text file and include the path to the related graphics files. This doesn 't work great for transferring files around, though.

  • Although a text file cannot contain graphics you could still use a text format using JSON or XML and then use the EncodeBase64 and DecodeBase64 functions to convert binary information such as pictures to text, inserting them into JSON/XML as appropriate, but this will make the files pretty large.

  • You could save the file as a binary file and embed everything in the file at specific locations, but this is less common these days.

  • You could use a SQLite database as the file format and create one or more tables to contain what you want to save. This is pretty useful and fully cross platform.

  • On MacOS there is a concept called a “package” that is essentially a folder that is treated as a file. This is not cross platform but is a way to combine different types of data. More information from Apple: Document Packages