Menu Editor

"FileView" Demo

in Visual Basic®




by Rick Meyer

Home

This demo is intended as a Menu Control introduction. When the project is completed you will have an excellent file browsing utility. Rather than dragging icons to the form, the menu control is created by using the Menu Editor in VB Tools. Pictured above is the editor.
      This project features:
  1. How to create and manage a dynamic menu.
  2. How to create and show a popup menu.

      Additionally this demo shows:
  3. How to run another program from VB.
  4. How to launch a file's associated program from VB.
  5. How to move a file to the recycle bin.
  6. How to move a file to another directory with Name.
  7. How to keep an INI file in the App.Path.
  8. How to find all files in a directory with Dir$.
  9. How to populate a ListView.
10. How to sort a ListView by clicking columnheaders.
11. How to sort numeric data in a ListView.
12. How to display any file in a textbox.
13. How to make all controls on a PictureBox invisible.
14. How to suppress textbox default right click popup.
15. How to dynamically resize ListView Columns.
16. How to proportion an image to fit inside a Form.

Program Operation is as Follows:
Navigation is accomplished primarily with the DirListBox. To save navigation time, the ListView is only populated on the SECOND CLICK on a directory. For the 10 most recently visited (populated in LV) directories, click an item in the Recent Menu.
Viewing a File merely click on the ListView item or use the arrow keys to highlight and press [Enter]. Click the text or press a key to return to the ListView.
Viewing a Picture make sure the Menu displays: "Show Picture" when clicking on a jpg, gif, bmp, or ico file, otherwise file contents are shown as text.
File Operations are possible by right clicking on the ListView item or the displayed text. You may then choose to: Delete, Rename, Run, or Edit the file.
  Instructions for Building this Project:

  1. Start a new standard exe.
  2. On Form1 put a TextBox named Text1
  3. Set the Text1.MultiLine property to True
  4. Set the Text1.Scrollbars property to Vertical
  5. On Form1 put an Image Control named Image1
  6. On Form1 put a PictureBox named Picture1
Important: The following controls go directly on Picture1
  7. On Picture1 put a TextBox named Text2
  8. On Picture1 put a Label named Label1
  9. On Picture1 put a Label named Label2
10. On Picture1 put a DirListBox named Dir1
11. On Picture1 put a DriveListBox named Drive1
12. On Picture1 put a ListView named ListView1
13. Set the ListView1.View property to 3 - lvwReport
14. Set the ListView1.FullRow Select = True

There is no need to position or size the above controls
on the form since that is all done in the Form_Load.

To find the ListView Control click on the VB Menu->Project->Components and select Microsoft Windows Common Controls 6. After you click OK the ListView Control (and some others) will appear in the toolbar usually on the left of the VB IDE (Integrated Development Environment).
Making the Menu:

With the Focus on Form1 (not code window)
Click VB Menu->Tools->Menu Editor (or press [Ctrl][E])

Where indicated: type in the text specified.
Otherwise mouse click as directed.

Caption: Recent   Name: mnuMain
Click [Next]

Click [->]
Name: mnuRecent   Index: 0
Click [Next]

Click [<-]
Name:  mnuPicOpt
Click [Next]

Name:  mnuLVpop
Click (uncheck) [Visible]
Click [Next]

Click [->]
Caption: View     Name: mnuView
Click [Next]

Caption: Delete   Name: mnuDelete
Click [Next]

Caption: Rename   Name: mnuRename
Click [Next]

Caption: Run      Name: mnuRun
Click [Next]

Caption: Edit     Name: mnuEdit
Click [OK]
The very first item you entered is pictured above.
The picture also shows how the Menu looks when completed.
Now you are ready for the code. Select all of the following code (by clicking on the word 'Option' three times) and copy it to the clipboard [Ctrl][Insert]. Then paste it into the code window of Form1 with [Shift][Insert]. Variable Key
% As Integer
& As Long
! As Single
# As Double
$ As String
Option Explicit

'==========================================================
'    The declarations necessary to recycle (delete)
'==========================================================
Const FO_DELETE = &H3
Const FOF_ALLOWUNDO = &H40

Private Type SHFILEOPSTRUCT
    hwnd As Long
    wFunc As Long
    pFrom As String
    pTo As String
    fFlags As Integer
    fAnyOperationsAborted As Long
    hNameMappings As Long
    lpszProgressTitle As Long
End Type

Private Declare Function SHFileOperation _
    Lib "shell32.dll" Alias "SHFileOperationA" ( _
        lpFileOp As SHFILEOPSTRUCT) As Long

'==========================================================
'    To disable TextBox default right click popup menu
'==========================================================
Private Declare Function LockWindowUpdate _
    Lib "user32" (ByVal hwndLock As Long) As Long

'==========================================================
'    The declaration necessary to Run and Edit files
'==========================================================
Private Declare Function ShellExecute _
    Lib "shell32.dll" Alias "ShellExecuteA" ( _
        ByVal hwnd As Long, _
        ByVal lpOperation As String, _
        ByVal lpFile As String, _
        ByVal lpParameters As String, _
        ByVal lpDirectory As String, _
        ByVal nShowCmd As Long) As Long

'==========================================================
'         General Program Declarations
'==========================================================
' Captions:
Const FMCAPTION = "File Viewer"
Const SHCAPTION = "Show Pictures"

' The minimum window width is set here
Const MINWD! = 9780

' A textbox can only hold a few more bytes than 57000
'  edit this if a RichTextBox is substituted.
Const MAXBYTES& = 57000

' Since the mnuRecent() array starts at 0, this constant
'  effectively allows 10 recent directories.
Const MAXRECENT = 9

' Format strings
Const F11 = "###,###,###", F14 = "##,###,###,###"

' Constants to Identify Columns
Private Enum columnEnum
    NAM = 1
    EXT
    SIZ
    DAT
End Enum

' Window position and size
Dim FmLt!, FmTp!, FmWd!, FmHt!

' Flags
Dim InViewFlag As Boolean
Dim KeyPressedElsewhere As Boolean

' Various other form level vars
Dim iniFile$, oldPath$, lastDir%, currSortKey%

'==========================================================
'                Click Menu Items
'==========================================================
Private Sub mnuRecent_Click(Index%)
    On Error GoTo md1
        
    'First change the Drive if necessary
    If LCase$(Left$(mnuRecent(Index).Caption, 1)) <> _
        LCase$(Left$(Drive1.Drive, 1)) Then
            Drive1.Drive = mnuRecent(Index).Caption
    End If

    'Next change the directory. By making equal
    ' to oldPath the ListView will be filled.
    oldPath = mnuRecent(Index).Caption
    Dir1.Path = oldPath
    Dir1_Click
    GoTo md2
    
md1: MsgBox mnuRecent(Index).Caption & " Error"
    Resume md2
md2: On Error GoTo 0
End Sub

Private Sub mnuPicOpt_Click()
    With mnuPicOpt
        Select Case .Caption
            Case SHCAPTION
                .Caption = "Don't " & SHCAPTION
            Case Else
                .Caption = SHCAPTION
        End Select
    End With
End Sub

'==========================================================
'                 Navigation Events
'==========================================================
Private Sub Drive1_Change()
    On Error GoTo DriveHandler
    Dir1.Path = CurDir(Drive1.Drive)
    Exit Sub

DriveHandler:
    Drive1.Drive = Dir1.Path
End Sub

Private Sub Dir1_KeyPress(KeyAscii As Integer)
    If KeyAscii = vbKeyReturn Then
        oldPath = Dir1.List(Dir1.ListIndex)
        Dir1_Click
    End If
End Sub

' 1. How to create and manage a dynamic menu.
Private Sub Dir1_Click()
    Dir1.Path = Dir1.List(Dir1.ListIndex)
    
    'The first click on a directory will not fill the
    '  Listview. 2nd click (on same directory) will.
    If oldPath <> Dir1.Path Then
        oldPath = Dir1.Path
        Exit Sub
    End If
    
    Dim j%, t$
    
    'The number of folders is handily returned
    '  by Dir1 - so show it in a label.
    t = CStr(Dir1.ListCount) & " folder"
    If Dir1.ListCount <> 1 Then t = t & "s"
    Label1.Caption = t
    
    'Check if this is one of the stored "recent" paths
    t = LCase$(oldPath)
    Do Until j >= lastDir
        If LCase$(mnuRecent(j).Caption) = t Then Exit Do
        j = j + 1
    Loop
    
    'Assign as necessary
    If j > MAXRECENT Then
        'Not found & at max - assign to last.
        j = MAXRECENT
    ElseIf j >= lastDir Then
        'Not found & not at max - create new.
        If j Then Load mnuRecent(j)
        mnuRecent(j).Enabled = True
        lastDir = lastDir + 1
    End If
    
    'Assignment if found - redundant but no problem
    mnuRecent(j).Caption = Dir1.Path
    ShuffleUp j
    FilesGet lvwAscending, NAM
End Sub

'Just bring the desired "Recent" choice up to index 0
Private Sub ShuffleUp(i%)
    Dim j%, sl$
    sl = mnuRecent(i).Caption
    
    For j = i To 1 Step -1
        mnuRecent(j).Caption = mnuRecent(j - 1).Caption
    Next
    
    mnuRecent(0).Caption = sl
End Sub

'==========================================================
'                 Events While Viewing
'==========================================================
Private Sub Form_KeyDown(KeyCode%, Shift%)
    Text1_KeyDown KeyCode, Shift
End Sub

Private Sub Text1_KeyDown(KeyCode%, Shift%)
    Select Case KeyCode
        Case 14 To 26, 28 To 31, 33 To 47
        Case Else
            KeyPressedElsewhere = True
            ViewOut
    End Select
End Sub

'14. How to suppress textbox default right click popup.
Private Sub Text1_MouseDown(Button%, Shift%, X!, Y!)
    If Button = vbRightButton Then
        ' Avoid the disabled gray text by locking updates
        LockWindowUpdate Text1.hwnd

        ' A disabled TextBox will not display default menu
        Text1.Enabled = False
        
        PopupMenu mnuLVpop
        
        Text1.Enabled = True
        LockWindowUpdate 0&
    End If
End Sub

Private Sub Text1_MouseUp(Button%, Shift%, X!, Y!)
    If Button = vbLeftButton Then ViewOut
End Sub

Private Sub Form_MouseUp(Button%, Shift%, X!, Y!)
    If Button = vbLeftButton Then ViewOut
End Sub

Private Sub Form_MouseDown(Button%, Shift%, X!, Y!)
    If Button = vbRightButton Then PopupMenu mnuLVpop
End Sub

Private Sub Image1_MouseUp(Button%, Shift%, X!, Y!)
    If Button = vbLeftButton Then ViewOut
End Sub

Private Sub Image1_MouseDown(Button%, Shift%, X!, Y!)
    If Button = vbRightButton Then PopupMenu mnuLVpop
End Sub

'==========================================================
'                  ListView Events
'==========================================================
Private Sub ListView1_KeyUp(KeyCode%, Shift%)
    Static j%
    
    'KeyPressedElsewhere is basically a fix ... where the
    ' keydown happened in another control, but when the
    ' key comes up you are subject to the suddenly visible,
    ' active, and focused Listview.
    
    Select Case KeyCode
        Case vbKeyReturn, vbKeySpace
            If KeyPressedElsewhere Then Exit Sub
            ListView1_MouseUp 1, 0, 0, 0
            KeyCode = 0
        Case vbKeyDelete
            mnuDelete_Click
            KeyCode = 0
        Case vbKeyF2
            mnuRename_Click
            KeyCode = 0
        Case vbKeyF4
            mnuEdit_Click
            KeyCode = 0
        Case vbKeyF5
            mnuRun_Click
            KeyCode = 0
        Case vbKeyEscape
            If KeyPressedElsewhere Then Exit Sub
            Unload Me
        Case Else
            CheckSelected
    End Select
End Sub

Private Sub ListView1_KeyDown(KeyCode%, Shift%)
    KeyPressedElsewhere = False
End Sub

' 2. How to create and show a popup menu.
Private Sub ListView1_MouseUp(Button%, Shift%, X!, Y!)
    Static j%
    
    j = CheckSelected
    If j = 0 Then Exit Sub
    
    If Button = vbRightButton Then
        SuppressEditChoice j
        PopupMenu mnuLVpop
        mnuEdit.Visible = True
    Else
        mnuView.Caption = "Back"
        SuppressEditChoice j
        Caption = Text2.Text
        
      Select Case LCase$(ListView1.ListItems(j).SubItems(1))
        Case "jpg", "gif", "jpeg", "bmp", "ico"
            If mnuPicOpt.Caption = SHCAPTION Then
                ShowPic
                Exit Sub
            End If
      End Select
        
        ShowText
    End If
End Sub

'These file types are not to editable.
' So that choice is just made unavailable.
Private Sub SuppressEditChoice(i%)
    Select Case LCase$(ListView1.ListItems(i).SubItems(1))
      Case "com", "exe", "pif", "zip"
          mnuEdit.Visible = False
    End Select
End Sub

'12. How to display any file in a textbox
Private Sub ShowText()
    Dim f%, j&, byts&, t$
    
    On Error GoTo st1
    f = FreeFile
    Open Caption For Binary As f
    byts = LOF(f)
    
    Select Case byts
        Case Is > MAXBYTES: byts = MAXBYTES
        Case 0: GoTo st2
    End Select
    
    t = Space$(byts)
    Get f, , t
    
    'Replace all Chr$(0) in the file with a space
    ' (that char terminates textbox display)
    For j = 1 To byts
        If Mid$(t, j, 1) = vbNullChar Then _
                Mid$(t, j, 1) = " "
    Next

    Text1 = t
    InViewFlag = True
    Picture1.Visible = False
    mnuMain.Visible = False
    mnuPicOpt.Visible = False
    Text1.Visible = True
    Text1.SetFocus
    GoTo st2
    
st1: Resume st2
st2: On Error GoTo 0
    Close
End Sub

'16. How to proportion an image to fit inside a Form.
Private Sub ShowPic()
    Const MNUHEIGHT! = 285
    Dim sc$, fh!, fw!, ph!, pw!
    Dim reread As Boolean
    
    On Error GoTo sp1
    
    InViewFlag = True
    Picture1.Visible = False
    mnuMain.Visible = False
    mnuPicOpt.Visible = False
    
    fw = ScaleWidth
    fh = ScaleHeight + MNUHEIGHT
    
    With Image1
        .Move 0, 0, fw, fh
        Set .Picture = LoadPicture(Caption)
    
        ph = Fix(ScaleY(.Picture.Height, _
                    vbHimetric, vbTwips))
        pw = Fix(ScaleX(.Picture.Width, _
                    vbHimetric, vbTwips))
        sc = "  Actual:" & picSize(pw, ph)
    
        If pw > fw Then
            If ph > fh Then
                'both dimensions > container
                If (pw / fw) > (ph / fh) Then
                    'if the ratio of the
                    'width > ratio of height
                    'then we need to limit
                    'the image height to
                    'maintain proportion
                    .Height = ph * fw / pw
                Else
                    'vice versa
                    .Width = pw * fh / ph
                End If
            Else
                'just the width > container
                .Height = ph * fw / pw
            End If
            reread = True
        ElseIf ph > fh Then
            'just the height > container
            .Width = pw * fh / ph
            reread = True
        Else
            'image will fit in container
            '  without change
            .Height = ph
            .Width = pw
        End If
    
        ph = .Height: pw = .Width
    
        'Center the image in the frame
        If ph < fh Then .Top = (fh - ph) / 2
        If pw < fw Then .Left = (fw - pw) / 2
    
        If reread Then
            Set .Picture = LoadPicture(Caption)
            sc = sc & " Shown:" & picSize(pw, ph)
        End If
    
        Caption = Caption & sc
        .Visible = True
    End With
    
    GoTo sp3
    
sp1: MsgBox Error(Err)
    Resume sp2
sp2: ViewOut
sp3: On Error GoTo 0
End Sub

Private Function picSize$(w!, h!)
    picSize = CStr(Fix(w / 15 + 0.5)) & "W x " & _
                CStr(Fix(h / 15 + 0.5)) & "H"
End Function

'10. How to sort a ListView by clicking columnheaders.
Private Sub ListView1_ColumnClick( _
        ByVal ColumnHeader As ColumnHeader)

    With ListView1
        If .ListItems.Count < 2 Then Exit Sub
        '11. How to sort numeric data in a ListView.
        '    The listview is an alpha sort - to make it
        '    work with numbers first pad with zeros
        If ColumnHeader.Index = SIZ Then ZerosPad
        
        'Column 4 is the date - displaying
        '  the most recent on the first sort.
        Select Case ColumnHeader.Index
            Case currSortKey: .SortOrder = 1 - .SortOrder
            Case DAT:         .SortOrder = lvwDescending
            Case Else:        .SortOrder = lvwAscending
        End Select

        'Do the actual sort and remember the column so if
        ' it is clicked again the order will be reversed.
        currSortKey = ColumnHeader.Index
        .SortKey = currSortKey - 1
        .Sorted = True
    
        'Remove the padded zeros
        If ColumnHeader.Index = SIZ Then ZerosOut
        .ListItems(1).Selected = True
    End With
End Sub

'==========================================================
'             Misc ListView Routines
'==========================================================
Private Function CheckSelected%()
    Static j%
    
    'Check the Listview items to see if one is selected
    For j = ListView1.ListItems.Count To 1 Step -1
        If ListView1.ListItems(j).Selected Then Exit For
    Next
    
    If j Then
        'Place full path in the long textbox
        Text2.Text = _
          FilePathName(mnuRecent(0).Caption, LVfilename(j))
    Else
        Text2.Text = ""
    End If
    
    CheckSelected = j
End Function

Private Function LVfilename$(ByVal num%)
    With ListView1.ListItems(num)
      LVfilename = Trim$(.Text) & "." & Trim$(.SubItems(1))
    End With
End Function

'==========================================================
'               Popup Menu Selections
'==========================================================
Private Sub mnuView_Click()
    If InViewFlag Then
        ViewOut
    Else
        ListView1_MouseUp 1, 0, 0, 0
    End If
End Sub

' 5. How to move a file to the recycle bin.
'    When delete is chosen - the file is moved
'    to the recycle bin - Kill does not
Private Sub mnuDelete_Click()
    Dim SHop As SHFILEOPSTRUCT
        
    If InViewFlag Then ViewOut
    If CheckSelected = 0 Then Exit Sub

    With SHop
        .wFunc = FO_DELETE
        .pFrom = Text2.Text
        .fFlags = FOF_ALLOWUNDO
    End With

    SHFileOperation SHop
    KeyPressedElsewhere = True
    If Dir$(Text2.Text) = "" Then _
        FilesGet ListView1.SortOrder, currSortKey
End Sub

' 6. How to move a file to another directory with Name.
'    Rename using an InputBox for dialog.
Private Sub mnuRename_Click()
    Dim j%, tmp$
    Dim startName$, newName$, startPath$, newPath$

    If InViewFlag Then ViewOut

    j = CheckSelected
    If j = 0 Then Exit Sub

    startName = Text2.Text
    startPath = LCase$(Parse(startName, tmp, "\"))
    
    KeyPressedElsewhere = True
    newName = InputBox("Change the path to move the file.", _
                        "Rename File", startName)

    newName = Trim$(newName)
    If newName = "" Then Exit Sub
    If newName = startName Then Exit Sub
    
    newPath = LCase$(Parse(newName, tmp, "\"))
    
    If Left$(startPath, 1) <> Left$(newPath, 1) Then
        MsgBox "Can not move to another drive."
        Exit Sub
    End If
        
    On Error GoTo r1
    Name startName As newName
    
    If startPath = newPath Then
        With ListView1.ListItems(j)
            .Text = Parse(tmp, tmp)
            .SubItems(1) = tmp
        End With
        CheckSelected
    Else
        FilesGet ListView1.SortOrder, currSortKey
    End If

    GoTo r2
    
r1: MsgBox Error(Err), , "Rename Error!"
    Resume r2
r2: On Error GoTo 0
End Sub

' 3. How to run another program from VB.
Private Sub mnuRun_Click()
    ShellExec False
End Sub

' 4. How to launch a file's associated program from VB.
Private Sub mnuEdit_Click()
    ShellExec True
End Sub

Private Sub ShellExec(EditFlag As Boolean)
    Dim j%, sn$, sp$
    
    j = CheckSelected
    If j = 0 Then Exit Sub
        
    If InViewFlag Then ViewOut
    sn = Text2.Text
    
    If EditFlag Then
      'Interestingly most file types are edited with their
      '  associated programs that "open" them. These are
      '  some exceptions that are edited with notepad.
      Select Case LCase$(ListView1.ListItems(j).SubItems(1))
        Case "htm", "html", "bat", "css", ""
            sp = sn
            sn = "notepad.exe"
      End Select
    End If
    
    ShellExecute hwnd, "open", sn, sp, mnuRecent(0).Caption, 1
End Sub

'13. How to make all controls on a PictureBox visible.
'    The Picture1 serves as container for Labels, Dir1,
'    Drive1 and Listview1. So when it is made visible
'    all those controls are visible as well.
Private Sub ViewOut()
    InViewFlag = False
    Text1.Visible = False
    Image1.Visible = False
    mnuMain.Visible = True
    mnuPicOpt.Visible = True
    mnuEdit.Visible = True
    Picture1.Visible = True
    mnuView.Caption = "View"
    Caption = FMCAPTION
    ListView1.SetFocus
    Text1.Text = ""
End Sub

'==========================================================
'               Populate the ListView
'==========================================================
Private Sub FilesGet(so As ListSortOrderConstants, sk%)
    Dim files%, filesize#, t$
    
    ListView1.ListItems.Clear
    ' 8. How to find all files in a directory with Dir$.
    t = Dir$(FilePathName(Dir1.Path, "*.*"))
         
    '    Get all the files in this loop
    Do Until t = ""
        files = files + 1
        filesize = filesize + FileAdd(t)
        t = Dir$
    Loop
    
    'The label with the directory bytes and num of files
    t = CStr(files) & " file"
    If files <> 1 Then t = t & "s"
    Label2.Caption = Format$(filesize, F14) & " bytes   " & t

    'Sort the listview by file name
    With ListView1
        .SortOrder = so
        .SortKey = sk - 1
        If sk = SIZ Then ZerosPad
        .Sorted = True
        If sk = SIZ Then ZerosOut
        If .ListItems.Count Then _
            .ListItems(1).Selected = True
        .SetFocus
    End With
    
    currSortKey = sk
    'Checkselected will fill the long testbox
    CheckSelected
End Sub

'This procedure does double duty by returning file size
Private Function FileAdd#(ByVal fn$)
    Static fName$, fExt$, LI As ListItem
    
    fName = Parse(fn, fExt)
    ' 9. How to populate a ListView.
    Set LI = ListView1.ListItems.Add(, , fName)
    fn = FilePathName(Dir1.Path, fn)
    
    With LI
        .SubItems(1) = fExt
        .SubItems(3) = Format$(FileDateTime(fn), _
                        "yyyy.mm.dd  hh:mm:ss")
        FileAdd = fSize(fn)
        .SubItems(2) = fn
    End With
End Function

Private Function fSize#(fs$)
    Static l&
    
    l = FileLen(fs)
    fs = Trim$(Format$(l, F11))
    If fs = "" Then fs = "0"
    
    fSize = CDbl(l)
End Function

'==========================================================
'            General String Handling
'==========================================================
Private Function FilePathName$(ByVal filepath$, filename$)
    FilePathName = AddSlash(filepath) & filename
End Function

Private Function AddSlash$(ByVal fp$)
    If Right$(fp, 1) <> "\" Then fp = fp & "\"
    AddSlash = fp
End Function

' Delim = "." Returns: fName ~ fx = fExtension
' Delim = "\" Returns: fPath ~ fx = fName.fExt
Private Function Parse$(ByVal fn$, fx$, _
                        Optional delim$ = ".")
    Static j%, l%
    
    fx = ""
    l = Len(fn)
    For j = l To 1 Step -1
        If Mid$(fn, j, 1) = delim Then Exit For
    Next
    
    If j > 1 Then
        If j < l Then fx = Right$(fn, l - j)
        fn = Left$(fn, j - 1)
    End If
    
    Parse = fn
End Function

Private Sub ZerosPad()
    Dim i%, j%, k%, s2$, s1$, t$
    
    With ListView1
        For i = 1 To .ListItems.Count
            s1 = Trim$(.ListItems(i).SubItems(2))
        
            k = 9
            s2 = String$(k, "0")
            
            For j = Len(s1) To 1 Step -1
                t = Mid$(s1, j, 1)
                If t <> "," Then
                    Mid$(s2, k, 1) = t
                    k = k - 1
                End If
            Next
            
            .ListItems(i).SubItems(2) = s2
        Next
    End With
End Sub

Private Sub ZerosOut()
    Dim j%, s$
    
    For j = 1 To ListView1.ListItems.Count
        With ListView1.ListItems(j)
            s = Format$(.SubItems(2), "###,###,###")
            If s = "" Then s = "0"
            .SubItems(2) = s
        End With
    Next
End Sub

'==========================================================
'          Initialization and Termination
'==========================================================
Private Sub Form_Load()
    Dim f%, sl$
    
    Caption = FMCAPTION
    Text1.Visible = False
    Text1.FontBold = True
    Text2.FontBold = True
    Image1.Stretch = True
    Image1.Visible = False
    Label1.FontBold = True
    Label2.Alignment = vbRightJustify
    Label2.FontBold = True
    Dir1.FontBold = True
    Drive1.FontBold = True
    Picture1.BorderStyle = vbBSNone
    ListView1.Font.Size = 10
    ListView1.Font.Bold = True
    mnuPicOpt.Caption = SHCAPTION
    
    With ListView1.ColumnHeaders
        .Add , , "Name"
        .Add , , "Type"
        .Add , , "Size    ", 1420, lvwColumnRight
        .Add , , "Date    \    Time    ", _
                            2300, lvwColumnRight
    End With

    'Defaults
    FmWd = MINWD
    FmHt = 6675
    mnuRecent(lastDir).Caption = "C:\"
    iniFile = FilePathName(App.Path, App.EXEName & ".ini")

    'Get the INI info
    On Error GoTo fl2
    f = FreeFile
    Open iniFile For Input As f
    'Get the window position and size
    Input #f, FmLt, FmTp, FmWd, FmHt

    'Get the recent directories
    Do Until EOF(f)
        Input #f, sl
        If Len(sl) > 1 Then
            If lastDir Then Load mnuRecent(lastDir)
            mnuRecent(lastDir).Caption = sl
            mnuRecent(lastDir).Enabled = True
            lastDir = lastDir + 1
        End If
    Loop

fl1: On Error GoTo 0
    Close
    
    'Sizing the form will initiate the resize event
    Move FmLt, FmTp, FmWd, FmHt
    Exit Sub

fl2: Resume fl1
End Sub

Private Sub Form_Resize()
    Const UNIT! = 240, DirWdth! = 2645
    Const UNITX2! = UNIT * 2, MINHT! = 3600
    
    If WindowState = vbMinimized Then Exit Sub
    If Width < MINWD Then Width = MINWD: Exit Sub
    If Height < MINHT Then Height = MINHT: Exit Sub
    
    FmLt = Left
    FmTp = Top
    FmWd = Width
    FmHt = Height

    Text1.Move 0, 0, ScaleWidth, ScaleHeight
    Picture1.Move 0, ScaleTop, ScaleWidth, ScaleHeight
    Dir1.Move 0, UNITX2, DirWdth, _
            ScaleHeight - Drive1.Height - UNITX2
    Drive1.Move 0, Dir1.Top + Dir1.Height, DirWdth
    Label1.Move 100, UNIT, 1040, UNIT
    Label2.Move ScaleWidth - 2700, UNIT, 2600, UNIT
    
    Dim lt!
    lt = Label1.Left + Label1.Width
    Text2.Move lt, UNIT - 60, Label2.Left - lt, UNIT
    
    '15. How to dynamically resize ListView Columns.
    With ListView1
        .Move DirWdth, UNITX2, ScaleWidth - DirWdth, _
                                ScaleHeight - UNITX2
        '270 needed for scrollbar width
        lt = .Width - 270 _
                - .ColumnHeaders(SIZ).Width _
                - .ColumnHeaders(DAT).Width
                
        .ColumnHeaders(NAM).Width = lt * 0.7
        .ColumnHeaders(EXT).Width = lt * 0.3
    End With
End Sub

Private Sub Form_Activate()
    mnuRecent_Click 0
End Sub

' 7. How to keep an INI file in the App.Path.
Private Sub Form_Unload(Cancel%)
    Dim f%, j%
    
    f = FreeFile
    Open iniFile For Output As f
    
    'Save the window position and size
    If WindowState = vbMinimized Then
        Write #f, FmLt, FmTp, FmWd, FmHt
    Else
        Write #f, Left, Top, Width, Height
    End If
    
    'Save the recent directories
    For j = 0 To lastDir - 1
        Write #f, mnuRecent(j).Caption
    Next
    
    Close
End Sub