![]() Home |
14. Open and Save Dialogs System Dialog Boxes |
![]() Home |
The Windows System has several common dialog boxes available from it's ComDlg32.DLL library, like the Open, Save, Color, Find, and Print. The Open and Save dialog boxes can be very useful and will be the subject of this lesson. In lesson number 8, about dialog boxes, you were told how to use templetes to make your own dialog boxes, and you could do the work and create your own folder search and file picker dialog box (open and save file). But since finding a file is a common task, you can call up a dialog box that the system already provides for finding and naming a File. The system Open and Save dialog boxes should be familar to many computer users, and have several options for the programmer to do some of the things they may need for a file search. There is even a creation option to place a child dialog on the these File dialogs, so you can add your own controls to customize it, but I can not talk about that in this lesson. |
Creating a System File Finder Dialog The system Open and Save file dialogs are like the "MessageBox" dialog because they are also modal, have a title, and have a window as their owner, but have many more options. The Open and Save dialogs are alike in their API methods for creation, use, and options. Although there are two different functions for the Open and Save dialog creation, GetOpenFileName( ) and GetSaveFileName( ), they both return a Bool value, and only take a single parameter, the TOpenFilename record. function GetOpenFileName(var OpenFile: TOpenFilename): Bool; function GetSaveFileName(var OpenFile: TOpenFilename): Bool; You can look up these functions in your API Help, but it does not give any info about the dialog options. I will not say much about these functions, except that they return "True" if they are successful and "False" if the user cancels the dialog or there is an error. All of the information about using these functions and changing the open dialog options is about the system OPENFILENAME or Delphi TOpenFilename record passed as the parameter. You set up how your file picker dialogs display and function by setting the TOpenFileName record. This TOpenFilename record has 20 elements (fields), and with this many elements, it can be confusing to you when you want to change dialog options (or just get it to work). You will need to read and review your Win32 API Help for index OPENFILENAME, hopefully you can get something about using these dialogs from all of the information they present.You may want to read the information at MSDN about this - Open and Save dialog boxes OPENFILENAME Structure The TOpenFilename record definition - type
POpenFilename = ^TOpenFilename;
tagOFNA = packed record
lStructSize: DWORD; // Cardinal
hWndOwner: HWND;
hInstance: HINST;
lpStrFilter: PChar;
lpStrCustomFilter: PChar;
nMaxCustFilter: DWORD;
nFilterIndex: DWORD;
lpStrFile: PChar;
nMaxFile: DWORD;
lpStrFileTitle: PChar;
nMaxFileTitle: DWORD;
lpStrInitialDir: PChar;
lpStrTitle: PChar;
Flags: DWORD;
nFileOffset: Word;
nFileExtension: Word;
lpStrDefExt: PChar;
lCustData: LPARAM; // Integer
lpFnHook: function(Wnd: HWND; Msg: UINT;
wParam, lParam: Integer): UINT stdcall;
lpTemplateName: PChar;
end;
TOpenFilename = tagOFNA;
OPENFILENAME = tagOFNA;There are eight text (PChar), eight DWORD (Cardinal), two Word, one Integer and one function (Pointer) element in this record. You will need to know something about setting the elements in this record before using it as a parameter. You should read through the API Help for OPENFILENAME to see what it says, but there is so many elements in this record, and so many flags and options for an open dialog, it is unlikely that you can understand what you need to set in these elements.Below I will give very short descriptions for the 20 elements in the TOpenFilename record, I can not begin to include much of the information that goes into using all of these elements and their interactions with other elements. Later I will present you with code examples that create an Open or Save Dialog, so you can read about what I did in the code to get the Dialog to work. The First example will be a "Basic" Open file dialog in the code for Basic Open Dialog below. In the element list below, you will see that the 9 elements used for the basic open dialog example code below are in a red color, the other 11 elements are not used and will just be zero and nil. List of the TOpenFilename record elements.
With 20 different elements, some with several various options, I can not try to give an explanation of how and why all of these may be used. You should be able to get some guidlines as to how and why these elements are set, when you read the API Help for OPENFILENAME, but they do not tell you which elements to set for a your specific dialog. It may be more usefull to look at the example codes below. To see which elements are set with which options. NOTE - In the Flag element options there is the OFN_EXPLORER flag bit, without this flag the dialog will use the old 16 bit windows open dialog interface. I will NOT have anything about creating or using the old 16 bit windows dialogs here, I will always use the OFN_EXPLORER flag for explorer (list view) style dialogs. First I will have some code that is used to show a typical "Basic", "Standard", non- modified, Open Dialog Box, in the DoOpenDlg function below. The DoOpenDlg function returns a String with the Open Dialog's pick of file path and name, or empty string if canceled. This code will set only nine of the elements in the TOpenFileName record, the other 11 elements are not needed for a basic Open Dlg and are set to zero or nil. I have included some comments in the code, as brief information about each TOpenFileName element, but you might need to read the Help for more information and other options. |
function DoOpenDlg: String;
var
OFName : TOpenFileName;
FileName: Array[0..2047] of Char; { Fixed Length Text Buffer for file path result.
You should use more than 256 chars for your text buffer, newer systems can
have file paths and names with kilobytes of charaters.
I mostly use 2 kilobytes}
FolderName: String;
begin
ZeroMemory(@FileName, SizeOf(FileName));
ZeroMemory(@OFName, SizeOf(OFName)); // Set all record elements to Zero or Nil
FolderName := 'C:/'; // this will be the first Folder shown in the Open Dialog
with OFName do
begin
lStructSize := SizeOf(OFName); // you MUST ALWAYS set the lStructSize
hWndOwner := hForm1; // Returns focus to the Owner window, usually main window
lpStrFile := @FileName; {IMPORTANT - you MUST set some kind of memory block
text buffer for the lpStrFile, This is where the
choosen file name and path is returned}
nMaxFile := SizeOf(FileName); // you MUST have the memory size of the lpStrFile
lpStrInitialDir:= PChar(FolderName); {the system will try and set the Open Dlg
first folder showing to this folder, if it exists, if it does not exist, or
you have this as nil, it uses the current system folder, this is optional}
Flags := OFN_EXPLORER or OFN_ENABLESIZING or
OFN_HIDEREADONLY or OFN_FILEMUSTEXIST;
{These Flags are some of the Usuall ones for an Open Dlg, their names give
some indication of what option they set}
lpStrFilter := 'Text files .TXT'#0'*.txt'#0'Delphi files, DPR,PAS,DOF'#0+
'*.dpr;*.pas;*.dof'#0'All files'#0'*.*'#0#0; // double null end
{the lpStrFilter are #0 terminated sets of strings which tells the Open
Dlg what files to show, there are two strings in each set, then first
string is displayed in the combo box, the second string has the ; separated
file extention filters, to show the end of these sets, use a double #0}
nFilterIndex := 1; { set your filter index, usually to One.
The filter index of zero if for the Custom Filter, which is
not used in this code. The filter index of One uses the first
filter in your lpStrFilter string above}
lpStrTitle := 'Search for a File to Open'; // Title on the Open Dlg, optional
end;
if GetOpenFileName(OFName) then // GetOpenFileName is True if successful
Result := FileName // FileName will have the file Path and Name in it
else
Result := ''; // Blank out Result string to show Failure of this function
{I could have used the code -
Result := OFName.lpStrFile;
instead of the -
Result := FileName;
the OFName.lpStrFile and the FileName are the same thing}
end;
|
|
This basic open dialog is enough for many, if not most, find and open a file dialogs. The comments above should help you to understand why some of the code was included. For the "Flag", I use only 4 of the more than 24 flags availible. I will now say something about the "Flag" options I have set above. Flags in DoOpenDlg - OFN_ENABLESIZING - I always use the OFN_ENABLESIZING flag, this allows the user to resize the dialog box, which are origially to small (for me) on larger screen resolutions. OFN_HIDEREADONLY - I almost always use the OFN_HIDEREADONLY flag, which causes the dialog to NOT show a "Open as Read Only" check box at the bottom of the dialog. I have found few times to need to use this "Open as Read Only" check box, and wonder why it is shown as the default. . . . This Flag name is usually confusing, many think it has something to do with read-only files, but it does not. OFN_READONLY - This flag is not in the DoOpenDlg, but is related to the OFN_HIDEREADONLY. If you include this Flag then the Read-Only Check Box will be checked OFN_FILEMUSTEXIST - In an Open Dialog (not in a Save dialog) I usually use the OFN_FILEMUSTEXIST, this flag causes the system to check for the existance of the file and give an error message if it does not exist. I use this flag in Open Dialogs, because you can not "Open" and use a file that does not exist. Your open dialog may not require the file to exist, so you would leave out ths flag. File Filter String- 'Text files .TXT'#0'*.txt'#0 The text before the first #0 will be shown in the combo list- Text files .TXT -in this case. Between the first and second #0 is- *.txt the File Filter Index, nFilterIndex Change DoOpenDlg( ) to a Save Dialog if GetOpenFileName(OFName) then You could use the same Flags, but it would not be a good set of flags for a save dialog, it seems better for a Save dialog to use - Flags := OFN_EXPLORER or OFN_ENABLESIZING or Save Flags - OFN_OVERWRITEPROMPT - Another useful Flag bit for a Save Dialog, which will cause the system to "Prompt" the user with a message box if that file name exists on the disk. To make sure they really want to replace it (OverWrite). Some Other Flags NOTE - You may have noticed (or not) that if you pick a system LINK file (with .lnk file extention) with the DoOpenDlg function, the Open dialog will NOT return the file path to that LINK, but the file path to the file that LINK is linked to. You can turn that off with the next Flag. OFN_NODEREFERENCELINKS - If you want the path to the LINK file and not the reference file of the link, then you will need to include the OFN_NODEREFERENCELINKS Flag. OFN_CREATEPROMPT - If that file does not exist, a dialog will be shown asking the user if he wants to create that file. This may be used to when a file name that exists is expected. OFN_NOCHANGEDIR - Normally the system will change the current working directory to where ever the user opens a folder in his search with an Open-Save dialog. If you use this Flag, then the system will restore the current working directory to its original value if the user changed the directory.
DlgOpenSave Unit Next is some code for a unit that has functions to display Open and Save dialogs. Some parts of this DlgOpenSave unit may be useful to use as a system Open-Save dialog unit for your programs that need an Open-Save Dialog. There are three functions in this unit - function OpenSavDlg(hOwner: Cardinal; const iniDirPath, Filter: String;
Open: BOOL): String;
function OpenDlgOpt(var DlgSetUp: TDlgSetUp;
FilterIndex: Cardinal = 1): String;
function OpenMultiSel(hOwner: Cardinal; const iniDirPath, Filter,
Title: String): TMultiResult;OpenSavDlg( ) - This function can do a simple open-save dialog, this returns a string with the choosen file path or an empty string if canceled or an error happens. It has four parameters - hOwner - Set to the window handle that get's focus on dialog close You can set the two string parameters (iniDirPath, Filter) to an empty string to get the default settings. If iniDirPath is an empty string, then it is set to your programs folder. If the Filter is an empty string, it is set to an "All Files" filter. You should be able to look at the function's code and see how it works. OpenDlgOpt( ) - This function uses a TDlgSetUp record as it's first parameter and an optional filter index as it's second parameter. It returns the file path of the choosen file if successful, or an empty string if canceled or an error. The TDlgSetUp record and TDlgOptions are defined as - TDlgSetUp = record
hOwner: Cardinal;
iniDirPath, iniFileName, Filter, Title, DefExt: String;
Options: Set of TDlgOptions;
end;
TDlgOptions = (doSave, doReadOnlyCB, doCheckRead, doFileExist,
doPathExist, doOverWrite, doCreatePompt, doLinks,
doNoChangeDir, doTrackFolder, doCusFilter);In the TDlgSetUp record, you should be able to get the idea of what the record element is used for from it's name. There are five string elements, if you do not place any text in these string elements, then default strings will be used if needed. You should be able to look at the function code and figure out what these strings set in the TOpenFilename. The Options element is a Set of TDlgOptions. You can look in the DlgOpenSave unit code below for some short descriptions of each of the TDlgOptions, and look at the function code in the unit to see what it is doing for these options. All of the elements in the TDlgOptions record are optional, you do not need to have any elements set in order to use it. Next is a code example to use the OpenDlgOpt( ) function. var
DlgSetUp1: TDlgSetUp;
with DlgSetUp1 do
begin
hOwner := hForm1;
iniDirPath := 'C:\Stuff';
iniFileName := 'NewFile1.txt';
Filter := 'Text files .TXT'#0'*.txt'#0'New Text files,+
' NewFile.TXT'#0'NewFile?.txt;NewFile??.txt'#0+
'All files'#0'*.*'#0#0;
Title := 'Save Something Else';
DefExt := '.txt';
Options := [doSave,doPathExist,doOverWrite,doCusFilter];
end;
fName := OpenDlgOpt(DlgSetUp1, 2);
if fName <> '' then
UseFName(fName);I tried to make this OpenDlgOpt( ) function easy to use and still have a good amount of options and factors availible for the dialogs. You should be able to take things out of the OpenDlgOpt( ) function that you do not use or add some option or setting you will use. OpenMultiSel( ) - This function will set the OFN_ALLOWMULTISELECT flag, so the Open dialog will be able to choose and return more than one file name. Because the returned file name string for mutiple files is different that the file name string returned for a single file, I have the result of this function as a TMultiResult record. This record has two elements, a fOffSet integer and a fNames string. For a mutiple file name result string, the system uses a #0 (null) delimited string, a #0 character will separate the folder path and each of the file names that follow it. The fOffSet integer will have the first file name character offset position or a -1 if user canceled or an error occured. You may not be used to using #0 delimited strings, like the ones returned for this function, so here is an example code procedure called DoMultiFile, to give you some ideas about how to separate each string segment. This example will use StrEnd( ) function to locate each of the #0 string delimters and place the file name string segment in a List Box. Code for the OpenMultiSel function - |
procedure DoMultiFile;
var
MultiRe: TMultiResult;
filePath: String;
pFileName: PChar;
begin
{the OpenMultiSel function uses a Multi-Selection dialog, the Result
string is different than a normal open dialog, it has null #0 delimited
file path and names}
MultiRe := OpenMultiSel(hForm1, 'C:\Stuff', 'Text files .TXT'#0'*.txt'+
#0'All files'#0'*.*'#0#0,'Open more than One File, Multi-Select');
{a TMultiResult is the result form a OpenMultiSel, the fOffSet will be
-1 if it fails, or the File-Name charater offset if it succeeds}
if MultiRe.fOffSet <> -1 then
begin
// I list the file name in the hListBox1 List Box
SendMessage(hListBox1, LB_RESETCONTENT, Zero, Zero);
SetString(filePath, PChar(MultiRe.fNames), MultiRe.fOffSet-1);
{ the Folder path is in the first section of the MultiRe.fNames
MultiRe.fNames is several #0 delimited only if more than one file
I use SetString( ) with the MultiRe.fOffSet-1 number to know it's length
if there is only ONE file name, the result string will not have #0
delimters, I test for the #0 delimter at MultiRe.fOffSet}
if MultiRe.fNames[MultiRe.fOffSet] <> #0 then // true if single file name
begin
SendMessage(hListBox1, LB_ADDSTRING, Zero,
Integer(@MultiRe.fNames[MultiRe.fOffSet+1]));
// the list box will add single string above using the file name offset
end else
begin
pFileName := StrEnd(PChar(MultiRe.fNames));
// StrEnd will get the #0 delimiter
Inc(pFileName); // move to character after #0
while pFileName^ <> #0 do // loop until there are two #0
begin
// add #0 terminated file name to list box
SendMessage(hListBox1, LB_ADDSTRING, Zero, Integer(pFileName));
pFileName := StrEnd(pFileName);
Inc(pFileName);
end;
end;
end else filePath := 'User Canceled or Error'; // MultiRe.fOffSet = -1
end;
|
![]()
Code for the DlgOpenSave unit
There are comments in this code that should give you information about what
that code block is used for. The OpenSavDlg( ) function is like the DoOpenDlg( )
function above, so I did not comment in it.
unit DlgOpenSave;
{this Unit has three functions to create system Open-Save Dialogs}
interface
uses Windows;
type
{ TDlgOptions are the 11 members which control the dialog creation Flags
in the TDlgSetUp-record Options set}
TDlgOptions = (doSave, doReadOnlyCB, doCheckRead, doFileExist,
doPathExist, doOverWrite, doCreatePompt, doLinks,
doNoChangeDir, doTrackFolder, doCusFilter);
TDlgSetUp = record // used for dialog Settings in the OpenDlgOpt function
hOwner: Cardinal; // replaced with the Filter Index when OpenDlgOpt returns
iniDirPath, iniFileName, Filter, Title, DefExt: String;
// the four strings above are optional
Options: Set of TDlgOptions;
end;
{setting the Options Set to include a TDlgOptions -
doSave - Makes a Save dialog, otherwize an Open dialog is shown
doReadOnlyCB - will show the Read-Only check box at bottom of dialog
doCheckRead - will check the read-Only check box, included in Option if user checks
doFileExist - system checks to see if file name exists, then asks user
doPathExist - system checks to see if file path exists, then asks user
doOverWrite - if file name exists, asks user if OK to over-write
doCreatePompt - if file name does not exist, asks if OK to create file
doLinks - will return the link .lnk file name, instead of the file it is linked to
doNoChangeDir - the current folder will not change with the dialog folder
doTrackFolder - places the folder path in the OpenFolder string when dialog closes
doCusFilter - Sets the CustomFilter array to save the user's custom filter}
TMultiResult = record // result record of the OpenMultiSel function
fOffSet: Integer; // File Name character off-set in fNames
fNames: String; // #0 delimited string with multi-FileNames
end;
// the OpenSavDlg is a simple Open-Save dialog creation function
function OpenSavDlg(hOwner: Cardinal; const iniDirPath, Filter: String;
Open: BOOL): String;
// the OpenDlgOpt can set many more dialog options with the DlgSetUp record
function OpenDlgOpt(var DlgSetUp: TDlgSetUp; FilterIndex: Cardinal = 1): String;
// the OpenMultiSel is a Multi-Selection Open dialog that returns a TMultiResult
function OpenMultiSel(hOwner: Cardinal; const iniDirPath, Filter,
Title: String): TMultiResult;
var
OpenFolder: String = 'C:'; // contains the Last Open Folder if doTrackFolder
CustomFilter: Array[0..511] of Char; // records the user custom filter if doCusFilter
implementation
uses Messages, CommDlg, SmallUtils;
const
Zero = 0;
One = 1;
All_Files: PChar = 'All files'#0'*.*'#0;
function OpenSavDlg(hOwner: Cardinal; const iniDirPath, Filter: String;
Open: BOOL): String;
var
OFName : TOpenFileName;
FileName: Array[Zero..2047] of Char;
begin
// basic open-save dialog creation
ZeroMemory(@FileName, SizeOf(FileName));
ZeroMemory(@OFName, SizeOf(OFName));
with OFName do
begin
lStructSize := sizeof(ofName);
hwndowner := hOwner;
nMaxFile := SizeOf(FileName);
lpstrFile := @FileName;
nFilterIndex := One;
if Length(iniDirPath) > One then
lpstrInitialDir := PChar(iniDirPath)
else
lpstrInitialDir := PChar(GetFilePath(ParamStr(Zero)));
if Length(Filter) < 4 then
lpstrFilter := All_Files
else
lpstrFilter := PChar(Filter);
if Open then
Flags := OFN_EXPLORER or OFN_FILEMUSTEXIST or OFN_HIDEREADONLY
else
Flags := OFN_EXPLORER or OFN_PATHMUSTEXIST or OFN_OVERWRITEPROMPT or
OFN_HIDEREADONLY;
end;
Result := '';
if Open then
begin
if GetOpenFileName(OFName) then
Result := FileName;
end else
if GetSaveFileName(OFName) then
Result := FileName;
end;
function OpenDlgOpt(var DlgSetUp: TDlgSetUp; FilterIndex: Cardinal = One): String;
const
// this FlagValues holds some of the constants for the OFName.Flags
FlagValues: array[TDlgOptions] of Cardinal = (Zero, Zero, OFN_READONLY,
OFN_FILEMUSTEXIST, OFN_PATHMUSTEXIST, OFN_OVERWRITEPROMPT,
OFN_CREATEPROMPT, OFN_NODEREFERENCELINKS,
OFN_NOCHANGEDIR, Zero, Zero);
var
OFName : TOpenFileName;
FilePath: Array[Zero..2047] of Char;
i: TDlgOptions;
procedure SetResult;
begin
//this procedure is used to set Result for both the Open and Save dialogs
Result := FilePath;
DlgSetUp.hOwner := OFName.nFilterIndex; { place the current Filter Index
into the hOwner incase you need to reset it to user's Index}
{ the doTrackFolder will record the folder path, so you can open the next
open-save Dlg in the same folder as the last open save}
if doTrackFolder in DlgSetUp.Options then
OpenFolder := GetFilePath(Result);
//the OFName.Flags will have the OFN_READONLY bit if the check box was checked
if OFName.Flags and OFN_READONLY <> Zero then
DlgSetUp.Options := [doCheckRead];
end;
begin
{this is a more flexable open dlg function, it has a TDlgSetUp record to
change the dialog with Owner, Title, Filter and other Options}
ZeroMemory(@FilePath, SizeOf(FilePath));
ZeroMemory(@OFname, SizeOf(OFName));
with OFName, DlgSetUp do
begin
lStructSize := sizeof(OFName);
hwndOwner := hOwner; // the owner is set to DlgSetUp.hOwner
nMaxFile := SizeOf(FilePath);
lpstrFile := @FilePath;
if Length(iniFileName) > One then
StrCopy(lpstrFile, PChar(iniFileName)); // set first file name here
if Length(iniDirPath) > One then
lpstrInitialDir := PChar(iniDirPath)
else // set default initial Folder to this programs folder
lpstrInitialDir := PChar(GetFilePath(ParamStr(0)));
if length(Filter) < 4 then
lpstrFilter := All_Files
else
lpstrFilter := PChar(Filter);
nFilterIndex := FilterIndex;
if length(Title) > Zero then
lpstrTitle := PChar(Title); // set dlg Title to OpenSet.Title
if length(DefExt) > One then
lpstrDefExt := PChar(DefExt);{lpstrDefExt will automatically add that file
ext to a file name with no ext}
Flags := OFN_EXPLORER or OFN_ENABLESIZING or OFN_HIDEREADONLY;
// start with default Flags for Explorer, Sizing and no CheckBox
if Options * [doCheckRead..doNoChangeDir] <> [] then
for i := doCheckRead to doNoChangeDir do
if i in Options then
Flags := Flags or FlagValues[i];
// the for loop above will place flag values in FlagValues array
// into the OFName.Flags if that option is in the set
if doReadOnlyCB in Options then // take out the read only checkbox
Flags := Flags and (not OFN_HIDEREADONLY);
if doCusFilter in Options then
begin // make a Custom Filter recorder availible
lpstrCustomFilter := @CustomFilter;
nMaxCustFilter := SizeOf(CustomFilter);
end;
Exclude(Options, doCheckRead); // Take out the Check Read option
end;
Result := '';
if doSave in DlgSetUp.Options then
begin
if GetSaveFileName(OFName) then
SetResult; // places the file path into Result
end else
if GetOpenFileName(OFName) then
SetResult;
end;
function OpenMultiSel(hOwner: Cardinal; const iniDirPath, Filter,
Title: String): TMultiResult;
var
OFName : TOpenFileName;
begin
{this will create a Multi-Selection Open Dialg box, and the Result of this
function is a TMultiResult record, which has the file-name OffSet as the
fOffSet element, used to extract the file names from the #0 delimited result
string in Result.fNames}
SetLength(Result.fNames, 3070); // larger file name buffer for multi files
ZeroMemory(@Result.fNames[One], Length(Result.fNames));
Result.fOffSet := -1; // set Result.fOffSet to an error result of -1
ZeroMemory(@OFName, SizeOf(OFName));
with OFName do
begin
lStructSize := sizeof(OFName);
hwndowner := hOwner;
hInstance := SysInit.hInstance;
nMaxFile := Length(Result.fNames);
lpstrFile := PChar(Result.fNames);
// adding the OFN_ALLOWMULTISELECT flag bit will change the file-path string returned
Flags := OFN_ALLOWMULTISELECT or OFN_EXPLORER or OFN_FILEMUSTEXIST or
OFN_ENABLESIZING or OFN_HIDEREADONLY;
if Length(Filter) < 4 then
lpstrFilter := All_Files
else
lpstrFilter := PChar(Filter);
lpstrTitle := PChar(Title);
nFilterIndex := One;
lpstrInitialDir:= PChar(iniDirPath);
end;
if GetOpenFileName(OFName) then
Result.fOffSet := OFName.nFileOffset // set fOffSet to the file name offset
else
Result.fNames := '';
end;
initialization
CustomFilter := 'Your Filter'#0'*.*'#0#0;
end. |
You should be able to create your own Open and Save dialog boxes,
and maybe a code unit for some open dialog functions.
![]()
Next Page
The next page shows you how to create and access Files on a Disk to Read and Write File Data.
15. Writing and Reading Files

H O M E 