Site hosted by Angelfire.com: Build your free website today!

Home
DelphiZeus
14. Open and Save Dialogs
System Dialog Boxes

Home

Using the System Open and Save Dialogs

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.
lStructSizeYou must always set this element to the size of this Record, 76 bytes
hWndOwnerShould be set to the handle of the window to get focus after the Dialog is closed
hInstanceThis can be Zero, unless you have the OFN_ENABLETEMPLATEHANDLE style flag is set and lpTemplateName is included. This is used to tell the system where to look for the child dialog template. Can be the program's or a library's hInstance.
lpStrFilterA PChar type, this Filter is a screening and file name elimination, based on file names or file extentions. You can have a text string in this parameter with different file name extentions that the user will see in the "files of type:" combo box on the open-save dialog box. This is a null (#0) character delimited string, with pairs of entries. See the instructions below in File Filter String for setting this string.
lpStrCustomFilterUsusally this is Nil. This is a string character storage area (buffer) for a custom-filter of files that the user can set to the file extentions he chooses to use and then re-use. The system can write to this buffer (if it is not nil), file extentions that the user types into the "file name:" edit box.
nMaxCustFilterIf your lpstrCustomFilter is not Nil, then you must tell the system to amount of bytes it can use to set text in the lpstrCustomFilter.
nFilterIndexUsually set to one. This is set for the Index of the dialog's filter combo box's current selection, but it is One based index and NOT a Zero based Index, since Zero for this element means the "Custom" filter will be used (if there is a custom filter). You can set or read this for the Index of the filter that is in the combo box.
lpStrFileThis PChar will be written to by the system when the user clicks the dialog's OK button, with the file name and path. You MUST have a memory block allocated to this PChar, for the system to write the choosen file name. You must also set the nMaxFile element to the size of this memory block.
nMaxFileYou will need to set this to the size in bytes of your lpstrFile charater memory. So the system will know to error out if there is not enough memory there for a file name and path.
lpStrFileTitleIf this PChar is not Nil, then the system will write the file name (no path) to this buffer.
nMaxFileTitleAs with all of the recieving text buffers here, you will need to set this to the size of the lpstrFileTitle if it is not Nil.
lpStrInitialDirIf this PChar element is not Nil, then the system will attempt to set the First folder location of the dialog to the folder path in this string. If it is nil, the open dialog will start in the current folder for your process. This current process folder is moved to the folder you happen to move to in these open-save dialogs.
lpStrTitleIf this PChar is not Nil, then the system will use the text here for the dialog's caption Title. If this is nil, an open dlg will have "Open" as the title, and a save dlg will have a title of "Save As".
FlagsYou can set the Flag bits to get different options for the dialog boxes, there are more than 20 flags and options, so I will not list them all here. You should look at your API Help for the list of Flag bit properties. You will see my explanations below for more about some of the different Flag bits.
nFileOffsetThis will have an offset (zero-based) from the beginning of the file path in lpStrFile, to the first character file name.
nFileExtensionThis will have an offset (zero-based) from the beginning of the file path in lpStrFile, to the first character file extention without the period.
lpStrDefExtThis PChar can be set to text for a "Default" file exention, to be added if the user types in a file name without any file extention. This may be helpfull for dialogs, with only one file type in it's filter but usually not needed for dialogs with a muti-type filter. This can only have a single file extention in it. This can not be changed with the file type combo box change, unless you do a OFN_ENABLEHOOK type of Open dialog.
lCustDataIf you use the OFN_ENABLEHOOK flag, this Integer can be used to hold whatever you need, as a container for your information, to pass to the lpfnHook function or read when the GetOpenFileName( ) function returns. If there is a OFN_ENABLEHOOK flag bit set and there is a lpfnHook function, then in the WM_INITDIALOG message of the lpfnHook function you can read the LParam as a Pointer to the TOpenFilename which you can read the lCustData element to get the Data you placed in this.
lpFnHookIf there is a OFN_ENABLEHOOK flag bit set, then you should set this element to your dialog message function, used to handle the dialog messages.
lpTemplateNameIf the OFN_ENABLETEMPLATE flag bit is set, then this is a PChar string that has the name for a dialog template resource in the module handle in the hInstance element, which must have a valid instance handle in it.

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.

Basic Open Dialog

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.
The first TOpenFileName record element lStructSize should always be set to the SizeOf(TOpenFileName). The purpose of the Open Dlg is to get a file path and name for a file, which the system will write into the lpStrFile element when the dialog box closes. This element is a Non-Initialized, zero-memory, PChar, so it can NOT be used untill you assign it some memory (you must always assign some memory to the lpStrFile). In this basic example I assign it to a Fixed length array of Char. But you have seen here before, that you can also use a PChar that you assign memory to with GetMem( ), or use a string variable that you have used SetLength( ) on to give it some memory. You must also set the nMaxFile element to the size in bytes of the memory buffer that you have assigned to lpStrFile.

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_EXPLORER and OFN_LONGNAMES - I always use the new 32-bit list-view dialog style of OFN_EXPLORER. With the OFN_EXPLORER flag, the OFN_LONGNAMES flag is automatically used so the full (32 bit long name) file path is returned with all of the disk path characters. So if you have OFN_EXPLORER you do not need the OFN_LONGNAMES.

  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-
The "File Filter" string lpStrFilter, should be included in most every dialog. This sets up the file filter combo box on your dialog that determines which file types to be displayed in the file selection list view. This string is a collection of pairs of null (#0) terminated strings, there are two null terminated strings for each list enrty in the dialog filter combo box. The first string in the pair is the text that is placed in the list of the File Types Combo Box, that the user sees. The second string of this pair is the semicolon  ;   delimited list of file extentions (or file names) to be shown for that file filter. The first pair of #0 delimted strings in my code is -

'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
This text is a "File Name" format, having the name then a period, then the file extention. Like -
    ReadMe.txt
The system will only show the files named ReadMe.txt . But you can use the same two text characters for system file name wild cards (asteric * and question mark ? ) that the FindFirstFile( ) function uses for it's wild card file names. So the text   '*.txt'   will show all files with the file extention of  TXT . If you wanted to show every file you would use two asterics * , , like '*.*' . You can include the space charater in these file names, although some API Help says that you can not use the space charater (in 16 bit dialogs you can NOT use the space charater). In the DoOpenDlg code above for lpStrFilter I have three entries (string pairs) for the file filter combo box,

the File Filter Index, nFilterIndex
To set the text shown in your dialog File Types Combo Box, you change the nFilterIndex element. To set the index for your filter strings, start with an index of One (not zero) for the first string pair. In the code above I set nFilterIndex to One to show the first entry- Text files .TXT . This nFilterIndex has a special Zero index, which is used for a "Custom Filter", I do not have any entry for a "Custom Filter" as lpStrCustomFilter in the code above, and many dialogs do not make any provision for a custom filter string. I will give some info about in the code for the program.

Change  DoOpenDlg( ) to a Save Dialog
To get a system Save Dialog instead of an Open Dialog , all of the code in the DoOpenDlg( ) function would remain unchanged, except, you would need to change the API function GetOpenFileName( ), , change the code line -

      if GetOpenFileName(OFName) then
to
      if GetSaveFileName(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
          OFN_HIDEREADONLY or OFN_PATHMUSTEXIST;

Save Flags -
  OFN_PATHMUSTEXIST - Is used instead of OFN_FILEMUSTEXIST, because a Save Dialog can get a file name that does not exist. And this Flag will tell the system to check the path the user has typed into the file name edit box, to see if the path exists and display and error message if it does not.

  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.

I will not give any information about any other TOpenFileName Flags in this lesson, the Flag information above is for all the Flags I use in this lesson. There are so many Flags, and several that you might never use. I will not use or give information about the Flags used for a "Hook" (dialog message function) dialog or any for network. I would recommend that you use the flags I have given above, to see how and if they do what you think they will do. You can read the help for the other Flags, there may be enough in the help description to allow you to try that flag, except for the OFN_ENABLEHOOK flag, which will require you to know how to use a Dialog Window Proc as described in Lesson 8 "Using Dialogs"

DlgOpenSave   Unit

Next is some code for a unit that has functions to display Open and Save dialogs.
You may can use this code to help you create your own code unit
for Open-Save dialogs with options you would usually use.

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
  iniDirPath - for the initial Folder, can be a folder path to start the search in.
  Filter - Has the pairs of #0 terminated text for the Filter of the dialog
  Open - Set to True for Open, False for Save dialog.

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.

I will not show the code here for a program that uses this DlgOpenSave.pas unit.
Instead I will make it availible in a Zip commpressed file as the OpenDlgU.pas file,
it also will have the DlgOpenSave.pas file.
download the   OpenSave.zip   File

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


       

Lesson -     One  Two  Three  Four  Five  Six  Seven  Eight  Nine  Ten  Eleven  Twelve  Thirteen  Fourteen




H O M E