|
Wenn Sie im Common Dialog-Steuerelement das Flag cdlOFNAllowMultiselect setzen, kann der Anwender mehrere Dateien im gleichen Ordner auf einmal auswählen. Eine solche Mehrfachauswahl wird in der FileName-Eigenschaft zurückgegeben.
With CommonDialog1
.CancelError = True
On Error Resume Next
.ShowOpen
If Err.Number = 0 Then
Debug.Print .FileName
End If
End With
Sie werden einen String zu sehen bekommen, der mit dem Pfad des Ordners beginnt und anschließend die Namen der gewählten Dateien. Die einzelnen Teile sind dabei durch Leerzeichen voneinander getrennt - so behauptet es jedenfalls die Visual Basic-Hilfe, und so sieht es offensichtlich auch aus. Dass die Behauptung der Hilfe so nicht stimmen kann, sollte Ihnen spätestens dann aufgehen, wenn Dateinamen ausgewählt worden sind, die selber schon Leerzeichen enthalten, oder wenn schon der Ordner-Pfad Leerzeichen enthält.
Es gibt zwar unter Windows die Konvention, dass Datei- und Pfadnamen, die Leerzeichen enthalten, komplett in Anführungszeichen eingeschlossen werden, und dass eine Trennung einer Aufreihung per Leerzeichen damit eindeutig unterscheidbar sei. Doch ist das Aufteilen mit der Notwendigkeit der Unterscheidung, ob sich ein Leerzeichen innerhalb oder außerhalb eines Anführungszeichenpaares befindet, zum einen recht mühselig. Und zum anderen: die Debug-Ausgabe der Rückgabe in der FileName-Eigenschaft enthält keinerlei Anführungszeichen und scheint sich also nicht an diese Konvention zu halten.
In Wahrheit ist die Behauptung der VB-Hilfe, dass das Leerzeichen das Trennzeichen sei, schlicht falsch. Sie war mal richtig, als seinerzeit unter Windows 3.x Dateinamen noch keine Leerzeichen enthalten durften, und daher der Kunstgriff zu umschließenden Anführungszeichen noch nicht existierte.
Tatsächlich werden die Teile durch Nullzeichen (Ascii 0, vbNullChar) voneinander getrennt. Und die Debug-Ausgabe führt uns lediglich auf Glatteis, indem sie das Nullzeichen als Leerzeichen darstellt. Die Teilung der Aufreihung lässt sich somit über die Split-Funktion (die allerdings erst seit VB 6 zur Verfügung steht) ganz einfach erledigen:
Dim nParts() As String
Dim l As Long
With CommonDialog1
.CancelError = True
On Error Resume Next
.ShowOpen
If Err.Number = 0 Then
nParts = Split(.FileName, vbNullChar)
For l = 0 To UBound(nParts)
Debug.Print nParts(l)
Next 'l
End If
End With
Das sieht schon viel besser und brauchbarer aus, nicht wahr? Als erstes Element mit dem Index 0 erhalten Sie den Pfad, und in den Elementen ab Index 1 die einzelnen Dateinamen.
Wenn Sie nun die Dateinamen noch mit vollständigen Pfad benötigen, können Sie den jeweiligen Dateinamen mit dem im ersten Element des Split-Arrays enthaltenen Pfad kombinieren. Sie sollten dabei allerdings nicht vergessen, dafür zu sorgen, dass der Pfad mit einem Backslash ("\") endet - einfach so immer einen Backslash einzufügen, ist nicht korrekt, da ein möglicherweise zurückgegebenes Wurzelverzeichnis eines Laufwerks bereits mit einem Backslash endet und dann zwei Backslashes aufeinander folgen würden.
Sie können nun in einem Schleifendurchgang dafür sorgen, dass alle Dateinamen mit dem vollständigen Pfad versehen sind. Und wenn Sie den Ordnerpfad anschließend nicht mehr benötigen, ignorieren Sie bei der weiteren Verarbeitung einfach das nullte Element des Arrays oder redimensionieren das Array mit der Angabe Preserve und dem Start-Index 1.
Dim nParts() As String
Dim l As Long
With CommonDialog1
.CancelError = True
On Error Resume Next
.ShowOpen
If Err.Number = 0 Then
nParts = Split(.FileName, vbNullChar)
If Right$(nParts(0), 1) <> "\" Then
nParts(0) = nParts(0) & "\"
End If
For l = 0 To UBound(nParts)
nParts(l) = nParts(0) & nParts(l)
Next 'l
ReDim Preserve nParts(1 To UBound(nParts))
End If
End With
Nun haben Sie das Array, das alle gewählten Dateien mit vollständigem Dateipfad enthält, nach Belieben verwenden. Nein, doch nicht. Denn falls der Anwender von Ihrem gut gemeinten Angebot, mehrere Dateinamen auszuwählen, keinen Gebrauch macht, sondern nur einen Dateinamen auswählt, tut das Common Dialog-Steuerelement leider so, als wäre das Mehrfachauswahl-Flag nicht gesetzt, und gibt in der Eigenschaft FileName lediglich den kompletten Pfad zu der einen gewählten Datei zurück. Und leider nicht wenigstens den Pfad und den Dateinamen getrennt, so dass die Behandlung auf die oben gezeigte Weise konsistent und ohne Bruch möglich wäre.
Sie müssen also noch diesen Fall unterscheiden und separat behandeln. In jedem Fall ist es sinnvoll, zunächst die Split-Funktion ihre Arbeit tun zu lassen. Ist die Obergrenze des von ihr zurückgegebenen Arrays 0, so wurde nur ein Dateiname ausgewählt, und Sie können die Sonderbehandlung anwenden.
Dim nParts() As String
Dim l As Long
With CommonDialog1
.CancelError = True
On Error Resume Next
.ShowOpen
If Err.Number = 0 Then
nParts = Split(.FileName, vbNullChar)
If UBound(nParts) = 0 Then
ReDim Preserve nParts(0 To 1)
nParts(1) = nParts(0)
ReDim Preserve nParts(1 To 1)
Else
If Right$(nParts(0), 1) <> "\" Then
nParts(0) = nParts(0) & "\"
End If
For l = 0 To UBound(nParts)
nParts(l) = nParts(0) & nParts(l)
Next 'l
ReDim Preserve nParts(1 To UBound(nParts))
End If
End If
End With
Bis auf die abschließende Redimensionierung zum Entfernen des ersten Elements mit dem Pfad erledigt die folgende Funktion CDlgFilePathsArr diese Arbeit für Sie. Ihr übergeben Sie den Inhalt der FileName-Eigenschaft des Common Dialog-Steuerelements und erhalten ein Array zurück, in dem alle Dateinamen mit vollständigem Pfad enthalten sind. Dieses Array enthält im ersten Element noch den "nackten" Ordner-Pfad - dieser endet aber in jedem Fall mit einem Backslash.
Public Function CDlgFilePathsArr(FileName As String) As String()
Dim nParts() As String
Dim l As Long
Dim nPos As Long
nParts = Split(FileName, vbNullChar)
Select Case UBound(nParts)
Case 0
ReDim Preserve nParts(0 To 1)
nParts(1) = nParts(0)
nPos = InStrRev(nParts(0), "\")
nParts(0) = Left$(nParts(0), nPos)
CDlgFilePathsArr = nParts
Case Is > 0
If Right$(nParts(0), 1) <> "\" Then
nParts(0) = nParts(0) & "\"
End If
For l = 1 To UBound(nParts)
nParts(l) = nParts(0) & nParts(l)
Next 'l
CDlgFilePathsArr = nParts
End Select
End Function
Falls Sie statt eines Arrays lieber eine Collection hätten, die die Dateipfade enthält, wird Ihnen diese von der folgenden Funktion CDlgFilePathsColl geliefert. Da eine Collection immer mit dem Index 1 beginnt, wäre hier kein sinnvolles Plätzchen für die Rückgabe des Ordner-Pfades vorhanden. Daher können Sie der Funktion optional eine String-Variable übergeben, die den Pfad zur weiteren Verwendung zurückgibt.
Public Function CDlgFilePathsColl(FileName As String, _
Optional Path As String) As Collection
Dim nParts() As String
Dim l As Long
Dim nPos As Long
Dim nColl As Collection
nParts = Split(FileName, vbNullChar)
Set nColl = New Collection
Select Case UBound(nParts)
Case 0
nPos = InStrRev(nParts(0), "\")
Path = Left$(nParts(0), nPos)
nColl.Add nParts(0)
Case Is > 0
If Right$(nParts(0), 1) <> "\" Then
nParts(0) = nParts(0) & "\"
End If
Path = nParts(0)
With nColl
For l = 1 To UBound(nParts)
.Add nParts(0) & nParts(l)
Next 'l
End With
End Select
Set CDlgFilePathsColl = nColl
End Function
Leider können Sie diese beiden Funktionen so nicht in Visual Basic 5 verwenden, da dort die beiden verwendeten Visual Basic-Funktionen Split und InstrRev noch nicht zur Verfügung stehen. Zwar könnten Sie entsprechende Ersatzfunktionen verwenden, wie Sie sie beispielsweise unter "Teile-Haberschaft" und unter "Retourkutsche" finden können. Sie können aber auch die folgenden Varianten der oben stehenden Funktionen verwenden, den Ersatz der besagten VB-Funktionen direkt und effizienter enthalten. Da in VB 5 jedoch ein Array als Rückgabewert einer Funktion noch nicht möglich ist, ist CDlgFilePathsArr hier als Prozedur angelegt, der Sie im zweiten Parameter ein deklariertes, aber noch nicht dimensioniertes String-Array zur Rückgabe übergeben.
Public Sub CDlgFilePathsArr(Filename As String, FilePaths() As String)
Dim l As Long
Dim nPos As Long
Dim nStart As Long
ReDim FilePaths(0 To Len(Filename) \ 10)
nStart = 1
Do
nPos = InStr(nStart, Filename, vbNullChar)
If nPos Then
If nStart = 1 Then
FilePaths(0) = Left$(Filename, nPos - 1)
If Right$(FilePaths(0), 1) <> "\" Then
FilePaths(0) = FilePaths(0) & "\"
End If
Else
l = l + 1
If UBound(FilePaths) < l Then
ReDim Preserve FilePaths(0 To UBound(FilePaths) + 10)
End If
FilePaths(l) = _
FilePaths(0) & Mid$(Filename, nStart, nPos - nStart)
End If
nStart = nPos + 1
Else
If nStart = 1 Then
ReDim FilePaths(0 To 1)
FilePaths(1) = Filename
For l = Len(Filename) To 1 Step -1
If Mid$(Filename, l, 1) = "\" Then
FilePaths(0) = Left$(Filename, l)
Exit For
End If
Next 'l
Exit Sub
Else
l = l + 1
If UBound(FilePaths) < l Then
ReDim Preserve FilePaths(0 To UBound(FilePaths) + 10)
End If
FilePaths(l) = FilePaths(0) & Mid$(Filename, nStart)
End If
End If
Loop While nPos
ReDim Preserve FilePaths(0 To l)
End Sub
Public Function CDlgFilePathsColl(Filename As String, _
Optional Path As String) As Collection
Dim l As Long
Dim nPos As Long
Dim nStart As Long
Dim nColl As Collection
Set nColl = New Collection
With nColl
nStart = 1
Do
nPos = InStr(nStart, Filename, vbNullChar)
If nPos Then
If nStart = 1 Then
Path = Left$(Filename, nPos - 1)
If Right$(Path, 1) <> "\" Then
Path = Path & "\"
End If
Else
.Add Path & Mid$(Filename, nStart, nPos - nStart)
End If
nStart = nPos + 1
Else
If nStart = 1 Then
.Add Filename
For l = Len(Filename) To 1 Step -1
If Mid$(Filename, l, 1) = "\" Then
Path = Left$(Filename, l)
Exit For
End If
Next 'l
Set CDlgFilePathsColl = nColl
Exit Do
Else
.Add Path & Mid$(Filename, nStart)
End If
End If
Loop While nPos
End With
Set CDlgFilePathsColl = nColl
End Function
Noch zwei Anmerkungen zum Schluss. Zum einen ist die Eigenschaft MaxFileSize des Common Dialog-Steuerelements auf eine Länge von 255 Zeichen voreingestellt. Dies reicht natürlich kaum aus, wenn mehr als ein, zwei handvoll Dateien ausgewählt werden. Damit die Eigenschaft FileName genügend Raum bekommt, alle ausgewählten Dateien ablegen zu können, sollte MaxFileSize auf einen genügend hohen Wert gesetzt werden, etwa 10.000 oder mehr. Zum anderen kann der Anwender natürlich nach wie vor auch manuell Dateinamen und -pfade in die Eingabe-Zeile des Dialogs einfügen. Da er aber kaum in der Lage sein wird, die Teile mit einem Nullzeichen voneinander zu trennen, wird die oben beschriebene Aufteilung natürlich kein brauchbares Ergebnis liefern. Den Fall zu unterscheiden, dass der Anwender die Teile mit Leerzeichen voneinander trennt und vorsorglich Pfade, die selbst Leerzeichen enthalten, konventionsgemäß in Anführungszeichen einschließt, wäre zwar auch möglich, aber recht aufwändig. Einfacher ist es, die anzugebenden Flags noch mit der Konstante cdlOFNFileMustExist zu verknüpfen, wodurch solche "eigenmächtigen" Eingaben schon vom Common Dialog-Steuerelement selbst zurückgewiesen werden.
|