Entwickler-Ecke
Open Source Units - TFolderBrowser (SHBrowseForFolder)
Delete - Fr 02.07.04 22:09
Titel: TFolderBrowser (SHBrowseForFolder)
So, da die Arbeiten an der TFolderBrowser-Klasse ganz offensichtlich abgeschlossen sind, möchte ich mal den aktuellen Status als Unit vorstellen. TFolderBrowser kapselt die API-Funktion "SHBrowseForFolder" und lässt sich sowohl für VCL- als auch für nonVCL-Projekte einsetzen. Basierend auf dem ursprünglichen
FAQ [
http://www.delphi-forum.de/viewtopic.php?t=25302]-Beitrag von Peter Lustig, entstand in diesem
Beitrag [
http://www.delphi-forum.de/viewtopic.php?t=27010&start=0] die Version im Anhang.
Hier das Beispiel für den normalen Aufruf:
Delphi-Quelltext
1: 2:
| fb := TFolderBrowser.Create(hwndDlg, 'Bitte wählen Sie einen Ordner'); |
Die Create-Methode hat noch mehr Parameter, die aber nicht zwangsläufig benutzt werden müssen, wie man ja auch sehen kann. Soll bspw. ein bestimmter Ordner vorausgewählt sein, wird er einfach als dritter Parameter angehangen,
Delphi-Quelltext
1: 2: 3:
| fb := TFolderBrowser.Create(hwndDlg, 'Bitte wählen Sie einen Ordner', 'c:\windows'); |
Moderiert von
MathiasSimmack: "hwndDlg" ist eine HWND-Variable in einer nonVCL-Anwendung. Bei einer VCL-Anwendung darf es auch gern "Handle", oder "self.Handle" oder "Application.Handle" sein!
Mit den beiden bool-Variablen kann man einstellen, ob die Dateien ebenfalls angezeigt werden sollen (ShowFiles), und ob der "Neuer Ordner"-Button sichtbar sein soll (NewFolder). Beides lässt sich aber auch später über die Properties der Klasse regeln. Aber erst mal weiter im Text:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| if(fb <> nil) then try if(fb.Execute) then MessageBox(hwndDlg,pchar(fb.SelectedItem), 'gewähltes Item',MB_OK or MB_ICONINFORMATION); finally fb.Free; end; |
Wie gesagt: ein üblicher Aufruf.
Daneben kann man aber auch die Anzeige eingrenzen, indem man ein anderes Root-Verzeichnis wählt. Standardmäßig entspricht die Anzeige ja dem Explorer-Baum. Für einen neuen Root gibt es eine überladene Funktion "SetRoot", der man entweder eine PItemIdList oder einen Pfad übergibt.
Um bspw. "Eigene Dateien" als Root zu setzen, genügt
Delphi-Quelltext
1:
| fb.SetRoot(CSIDL_PERSONAL); |
Die entsprechenden Konstanten lassen sich in Microsofts MSDN bzw. PSDK finden. Für einen normalen Ordner verwendet man einfach dessen Namen
Delphi-Quelltext
1:
| fb.SetRoot('C:\Mein Ordner'); |
Einer der Wünsche von Aton war ja ein Filter, durch den der OK-Button nur dann aktiviert wird, wenn im gerade ausgewählten Verzeichnis min. eine Datei des gewünschten Typs vorhanden ist. Dieser Filter lässt sich durch die gleichnamige Eigenschaft setzen.
Ein Filter sieht so aus:
mehrere Filter müssen durch das #0-Zeichen voneinander getrennt werden:
Delphi-Quelltext
1:
| fb.Filter := '*.txt'#0'*.*htm*'#0'*.xml'; |
Und zum Abschluss noch eine kleine Besonderheit: Windows XP besitzt eine eingebaute Linkverfolgung. D.h., wenn man im Dialog eine Verknüpfung (*.lnk) auswählt (ShowFiles = true), dann liefert der Dialog nicht den Namen der Verknüpfung sondern den Namen der dahinter liegenden Datei zurück. Dieses Verhalten kann man umgehen, wenn man die Eigenschaft
Delphi-Quelltext
1:
| fb.NoTargetTranslation := true; |
setzt. In dem Fall liefert nun auch XP den Namen der ausgewählten Verknüpfung zurück.
Für die älteren Windows-Versionen wurde aber auch eine Funktion eingebaut, die eine Verknüpfung verfolgen kann und die dahinter liegende Datei zurückliefert. Diese Funktion heißt "TranslateLink" und erwartet als Parameter den Namen der Verknüpfung. Hier ein Beispiel für das Startmenü, wobei eben bitte eine der Verknüpfungen (*.lnk) auszuwählen ist. Ansonsten macht´s nicht viel Sinn ;):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| fb := TFolderBrowser.Create(hwndDlg, 'Bitte wählen Sie einen Ordner'); if(fb <> nil) then try fb.SetRoot(CSIDL_STARTMENU); fb.ShowFiles := true; fb.NoTargetTranslation := true;
if(fb.Execute) then begin MessageBox(hwndDlg,pchar(fb.SelectedItem + ' -> ' + fb.TranslateLink(fb.SelectedItem)),'Verknüpfungsziel', MB_OK or MB_ICONINFORMATION) end; finally fb.Free; end; |
So, das war´s.
Vorschläge, Kritik usw. sind aber dennoch immer willkommen.
Nightmare_82 - Mo 12.07.04 14:13
kann man damit nur Ordner wählen oder auch Dateien ?
Wenn auch Dateien funktionieren, fände ich es praktisch, wenn man die Möglichkeit hätte, mehrere Dateien/Ordner auswählen zu können, also eine "Multiselect"-Property.
Ansonsten sieht das sehr brauchbar aus, ich werde es mal testen und in meinen Editor einbinden.
Delete - Mo 12.07.04 14:33
Ursprünglich diente der Dialog nur dazu einen Ordner auszuwählen. Warum ihn Microsoft so erweitert hat, dass man auch Dateien auswählöen kann, weiß der Geier. Zum Dateien - auch mehrer - auswählen ist immer noch der normale Dialog vorgesehen.
hitstec - Sa 14.08.04 21:36
Was genau macht dieser Code?
Quelltext
1:
| if(CoInitialize(nil) = S_OK) then |
Unter Delphi5 und WinXP Pro führt dieser Boolesche Ausdruck zu false. Entferne ich die if-Abfrage, dann klappt alles wunderbar.
@Mathias:
Dein Beispiel-Code enthält einen Fehler:
Quelltext
1:
| fb := TFolderBrowser.Create(hwndDlg); |
Der Construkter mit dieser Signatur existiert nicht ... es muss noch mind. ein String als Parameter übergeben werden.
:D
Delete - Sa 14.08.04 23:42
hitstec hat folgendes geschrieben: |
Was genau macht dieser Code? |
"COINITIALIZE" wird im allgemeinen aufgerufen, damit man COM-Objekte erzeugen/benutzen kann. Wie du ja auch sehen kannst, wird danach mit "IShellLink" gearbeitet, um ggf. die Datei zu ermitteln, auf die eine Verknüpfung zeigt.
Bei der VCL kannst du zu 99% davon ausgehen, dass dieser Befehl irgendwo mitgeladen wird. Da aber nichts schlimmes passiert, wenn man es mehrfach macht, habe ich es mir angewöhnt, den Code immer selbst zu schreiben.
Ich habe bspw. einen Fall, in dem
muss ich den Code zweimal verwenden. Einmal in meinem Programm, damit ich ebenfalls auf COM-Objekte zugreifen kann. Ein zweites Mal in einem Thread, der ja separat vom Programm läuft und daher auch eine separate Initialisierung der COM-Bibliothek erfordert.
Zitat: |
Unter Delphi5 und WinXP Pro führt dieser Boolesche Ausdruck zu false. |
Nicht bei mir. Und ich habe mein o.g. Programm mit den beiden Aufrufen von "CoInitialize" a) mit Delphi 5 geschrieben und b) oft genug unter XP gestartet.
Zitat: |
@Mathias:
Dein Beispiel-Code enthält einen Fehler: |
Ist korrigiert. Danke.
hitstec - Di 17.08.04 18:24
Titel: CoInitialize
Also ich habe so eben die Funktion an 4 verschiedenen Computer getestet, davon 2 Win2000 und 2 WinXP. Alle 4 liefern beim Aufruf dieser Funktion S_FALSE.
Microsoft empfiehlt die Funktion CoInitializeEx zu verwenden, die teilt mir wenigstens mit, dass eine andere Instanz läuft, liefert also RPC_E_CHANGED_MODE.
Sicherheitshalber würde ich statt
Quelltext
1:
| if (CoInitialize(nil)=S_OK) then |
lieber
Quelltext
1:
| if not (CoInitializeEx(nil,irgendeinekonstante)=S_FALSE) then |
verwenden.
Gruß
Delete - Mi 18.08.04 08:19
Dann ist das aber kein Fehler in der o.g. Klasse (also kein Bug im klassischen Sinn), sondern es liegt daran, dass dein Programm bereits irgendwo "CoInitialize(Ex)" aufruft. Übrigens besitzt auch "CoInitialize" den genannten Rückgabewert.
Und wenn, dann würde ich anstelle von
Delphi-Quelltext
1:
| if not(CoInitializeEx(nil,...) = S_FALSE) |
wohl eher
Delphi-Quelltext
1:
| if CoInitializeEx(nil,...) <> S_FALSE |
benutzen.
Ich mach´s so: Ich muss jetzt weg. Aber sobald ich wieder daheim bin, schau ich mir die Sache an, probiere es selbst noch mal richtig und ausführlich aus, fixe es ggf. und ändere das erste Posting entsprechend. Auf jeden Fall Danke für deine Infos. :)
hitstec - Mi 18.08.04 09:36
Oki! :P
Delete - Mi 18.08.04 16:20
Okay, ich hab´s jetzt wie folgt geändert:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| hr := CoInitializeEx(nil,COINIT_APARTMENTTHREADED); if(hr = S_OK) or (hr = S_FALSE) then try finally CoUninitialize; end; |
(s. auch erstes Posting) Soll bedeuten: Wenn das Ergebnis S_OK istm dann wurde die COM-Bibliothek initialisiert. Ist das Ergebnis S_FALSE, dann war sie schon initialisiert. Aber in beiden Fällen, so das PSDK, muss "CoUninitialize" aufgerufen werden.
Ich habe es mit einem recht sinnlosen Beispiel (;)) getestet, bei dem ich auch schon im Hauptteil "CoInitialize" aufgerufen habe. Während vorher, wie du ja auch bemängelt hast, der Dialog gar nicht erschien (weil eben nur auf S_OK geprüft wurde), funktionierte die Klasse nach dem kleinen Eingriff wieder wie gewünscht.
Ja, nun bist du wieder dran ... :)
hitstec - Mi 18.08.04 18:42
So ists recht. Mehr habe ich nicht mehr hinzuzufügen! :o
MarkusZi - Sa 04.06.05 17:22
Wie kann ich fb auch an eine andere Stelle verschieben?
fb.top und fb.left sind ja nicht möglich!
Christian S. - Sa 04.06.05 17:38
Hallo!
Indem Du in der Callback-Funktion SetWindowPos aufrufst. Ich habe die Klasse mal um eine entsprechende Funktionalität erweitert. Wenn beide Eigenschaften (left und top) vor dem Anzeigen gesetzt werden, so wird die entsprechende Position gesetzt. Du findest sie im ersten Posting.
Grüße
Christian
//edit: Mit Mathias' Erlaubnis die Unit ins erste Posting eingefügt
MarkusZi - Sa 04.06.05 18:31
Super, Danke zetzt geht´s !
Delete - Sa 20.08.05 08:55
Weil die Frage aufkam, noch mal zwei Punkte:
1.
TFolderBrowser ist
keine Komponente. Es ist einfach nur eine Unit, die man wie gewohnt in sein Programm einbindet:
2. Nachdem das erledigt ist, braucht ihr eine Variable vom Typ
TFolderBrowser, die ihr in einem Buttonklick o.ä. unterbringt, in dem ihr dann auch die Klasse selbst erzeugen und aufrufen könnt. Die absolut einfachste Variante würde so aussehen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| procedure TForm1.Button1Click(Sender: TObject); var fb : TFolderBrowser; begin fb := TFolderBrowser.Create(hwndDlg,'Bitte wählen Sie einen Ordner'); if fb <> nil then try if fb.Execute then ShowMessage(Format('gewählter Ordner: "%s"',[fb.SelectedItem])); finally fb.Free; end; end; |
Wie gesagt, das ist nur ein Beispiel. Die Unit funktioniert auch in anderen Situationen, nicht nur bei Buttonklicks ;). Und sie lässt sich auch für nonVCL-Programme verwenden.
Delete - Di 13.09.05 11:03
Nach Anregung von Christian hier noch eine kleine Neuheit: Microsoft Office und auch Openoffice 2 verwenden spezielle Verknüpfungen, deren Ziel (sprich: die eigentliche Datei) sich nicht ohne Weiteres ermitteln lässt. Diese Verknüpfungen sind eine Besonderheit des Microsoft Installer.
Nach ein bisschen Knobeln kam Christian auf die Idee, wie man dennoch an den Dateinamen herankommt, und ich habe das ganze in Form der Funktion "TranslateMsiLink" in die Unit eingebaut. Die aktuelle Version gibt es im allerersten Beitrag, hier nur das obligatorische Beispiel. Zum Testen bitte eine Office-Verknüpfung wählen (Word, Excel, Frontpage, usw.) oder etwas ähnliches (Openoffice 2):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
| fb := TFolderBrowser.Create(Handle, 'Bitte wählen Sie einen Ordner'); if(fb <> nil) then try fb.SetRoot(CSIDL_COMMON_STARTMENU); fb.ShowFiles := true; fb.NoTargetTranslation := true;
if(fb.Execute) then begin tmp := fb.TranslateMsiLink(fb.SelectedItem);
if(tmp = '') then tmp := fb.TranslateLink(fb.SelectedItem);
MessageBox(hwndDlg, pchar(fb.SelectedItem + ' -> ' + tmp), 'Verknüpfungsziel', MB_OK or MB_ICONINFORMATION) end; finally FreeAndNil(fb); end; |
ash0r - Fr 03.03.06 15:01
hi
ist echt n tolles ding und ich konnte dank dem fb viele probleme loesen...
hab mir erlaubt daraus eine komponente zu basteln...
sind nur paar kleine aenderungen , die ich vornehmen musste.
und hab sonst nichts geaendert ... weder die hinweise oben ... noch irgendwie meinen namen reingeschrieben :)
dachte nur , es waere so nuetzlicher!
hoffentlich ist das okay fuer die entwickler :)
ansonsten bescheidgeben..
mfg ..
Delete - Fr 10.03.06 10:40
Das einzige Problem, das ich mit deiner Komponente habe, ist die Unmenge an neuen Units und der kleine Bock ;), den du bei "FileExists" geschossen hast. Wenn schon die SysUtils-Unit, dann kann meine API-"fileexists"-Funktion natürlich komplett entfallen.
Ich habe mir also mal erlaubt, auf der Basis der originalen Klasse den notwendigen Code für eine Komponente zu ergänzen. Ich habe den Compilerschalter
benutzt. Lässt man den Punkt, dann entsteht die normale Klasse. Entfernt man ihn, erhält man eine Komponente für das Formular. Zusätzlich habe ich folgende Eigenschaften ergänzt, die man auch per Objektinspektor ändern können sollte:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| TFolderBrowser = class(TComponent) published property Caption: string read FCaption write FCaption; property PreselectedFolder: string read FInitFolder write FInitFolder; property Top: integer read FTop write SetTopPosition default 0; property Left: integer read FLeft write SetLeftPosition default 0; end; |
Da ich aber nicht (mehr) allzu firm in Sachen VCL und Komponenten bin, bitte ich um eine Kontrolle und ein ausgiebiges Testen. Das Bildchen habe ich erst mal weggelassen, das könnt ihr notfalls aus dem Beitrag (ZIP-Datei) von ash0r bekommen, oder ihr gebt euch erst mal mit dem Standardbild von Borland zufrieden.
ash0r - Fr 10.03.06 16:45
hast recht , ich musste diese funktion etwas aendern:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| function fileexists(const FileName: string): boolean; var Handle : THandle; FindData : TWin32FindData; begin Handle := FindFirstFile(pchar(FileName),FindData); Result := (Handle <> INVALID_HANDLE_VALUE);
if(Result) then FindClose(Handle); end; |
funktioniert es denn immer noch so , wie es sollte ?
hab vorhin versucht deine pas datei hier zu installieren...
hat aber nicht hingehauen... irgendwie kriege ich die komponente nicht installiert...
und noch was..
wenn ich jetzt die von mit gemachte komponente verwende kriege ich folgende warnung:
Quelltext
1:
| [Warnung] fldbrows.pas(73): Methode 'Create' verbirgt virtuelle Methode vom Basistyp 'TComponent' |
es funtzt zwar alles aber die meldung ist dennoch da...
weisst du ne loesung?
sorry bin noch net so erfahren in delphi... :)
mfg...
Moderiert von
Christian S.: Code- durch Delphi-Tags ersetzt
Delete - Sa 11.03.06 10:44
ash0r hat folgendes geschrieben: |
hast recht , ich musste diese funktion etwas aendern:
Delphi-Quelltext 1: 2:
| function fileexists(const FileName: string): boolean; | |
Du hättest diese Funktion komplett weglassen können, denn die SysUtils-Unit enthält bereits eine "FileExists"-Implementation. Da die ursprüngliche Klasse aber auch für nonVCL-Programme gedacht war, war die Idee, das entstehende Programm so klein wie möglich zu halten. Darum der Verzicht auf SysUtils und ähnliche Dickmacher.
Zitat: |
hat aber nicht hingehauen... irgendwie kriege ich die komponente nicht installiert... |
Hat bei mir geklappt. Ich habe noch für Delphi 5 so eine Art DPK-Datei, in die ich meine Unit eingefügt habe. Nach dem Kompilieren hatte ich dann in der Symbolleiste einen neuen Reiter namens "FolderBrowser" mit dem entsprechenden Button und dem Standardbild von Borland. Auf die Form gezogen, Eigenschaften zugewiesen, per Buttonklick gestartet ... kein Problem.
Zitat: |
wenn ich jetzt die von mit gemachte komponente verwende kriege ich folgende warnung:
Quelltext 1:
| [Warnung] fldbrows.pas(73): Methode 'Create' verbirgt virtuelle Methode vom Basistyp 'TComponent' |
es funtzt zwar alles aber die meldung ist dennoch da... |
Wenn du mal guckst, ich habe in meiner PAS-Datei diese Methode per Compilerschalter ausgeklammert, wenn die Unit als Komponente kompiliert wird. Ich habe übrigens auch erwähnt, dass du den Punkt beim Compilerschalter
{.$DEFINE VCLCOMPONENTMODE} entfernen
musst, damit eine Komponente ensteht! Die
Create-Methode war auch wieder für die Klasse gedacht, weil man bei ihr gleich Beschreibung und Ordner usw. übergeben konnte (s. auch Quellcodebeispiele in den obigen Beiträgen).
Zitat: |
sorry bin noch net so erfahren in delphi... :) |
:lol: Und ich nicht mehr, denn mittlerweile arbeite ich mehr mit CSharp.
vsh - Mo 03.04.06 13:31
Titel: bug report
Ich bin mir nicht ganz sicher, ob das jetzt hier die richtige Stelle dafür ist, aber ich habe einen kleinen bug gefunden.
wenn man im folderbrowser einen "neuen ordner" anlegt und diesen dann zB mit F2 umbenennt und während man noch in der "umbenennen-phase" ist "Ok" drückt, dann kommt ein fehler. Ich schätze mal das an irgendeiner stelle eine aktualisierung zu wenig ist, denn diese vorgehensweise ist nicht so untypisch und bei allen mir bekannten anwendungen geht es auch.
mfg vsh
Delete - Di 04.04.06 10:41
Ich kann bei mir, Win XP Pro + SP2, in der "Umbenennen-Phase" gar kein OK drücken, weil der Button deaktiviert ist. :gruebel:
Delete - Fr 07.04.06 10:21
Also, mit einem Standardaufruf bleibt der OK-Button auch im Editiermodus aktiv. Mein Problem (s. letztes Posting) war der Knopf im Beispielprogramm, bei dem zusätzlich nach bestimmten Dateitypen gesucht wurde. Darum der deaktivierte OK-Button.
Trotzdem kann ich den Fehler nicht nachvollziehen. Auch wenn ich mich im Editiermodus befinde, ein paar Buchstaben anhänge (meinetwegen
Neuer Ordner123) und dann OK klicke, dann wird der Ordner mit eben diesem Namen ohne Fehler erzeugt.
Ich brauche also etwas mehr Code von deiner Seite, weil ich gern wissen möchte, wie du den Dialog aufgerufen hast und was danach noch kommt. Bitte poste deinen eigenen Code aber
nicht hier in meinem Beitrag, sondern erstelle bitte ein eigenes Thema in
dieser [
http://www.delphi-forum.de/forum_Dateizugriff_17.html] Sparte.
Jakob Schöttl - Sa 10.06.06 20:02
Tolle Unit!
Aber so was wird ja öfter gebraucht, bei vielen Anwendungen...
Ich frag mich, warum das noch keine Standard-Komponente in der Registerkarte "Dialog" von Delphi ist.
humbuk - Do 09.11.06 20:19
Hallo,
bin mir auch nicht sicher ob ich ein bug gefunden habe.
Wenn ich fb.Execute das erste Mal aufrufe und dann auf abrrechen drücke wird False zurückgegeben. Bei nachfolgenden aufrufen wird auch bei Abbrechen True zurückgegeben, weil in FSelected noch der Pfad vom letzten Aufruf gespeichert ist. Hab das erstmal gelöst, indem ich den String in FSelected vor dem Dialogaufruf lösche.
Hier der Auszug aus der geänderten fldbrows.pas
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| ... FSelected := ''; pidlResult := SHBrowseForFolder(BrowseInfo); if(pidlResult <> nil) then begin if(FSelected = '') then if(SHGetPathFromIdList(pidlResult,Path)) and (Path[0] <> #0) then begin FSelected := Path; Result := true; end;
self.FreeItemIdList(pidlResult); end; ... |
Und hier noch ein Auszug aus meinem Programm:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| fb := TFolderBrowser.Create(self.handle, 'Please choose the directory of the LCC-Compiler', ExtractFilePath(ParamStr(0)) );
if(fb <> nil) then try repeat if( fb.Execute ) then if not FileExists( fb.SelectedItem + '\bin\lcc.exe' ) then ShowMessage('lcc.exe was not found in subdirectory \bin!' + #13#10 + 'Please select again.' ) else WriteDir( fb.SelectedItem ) else begin break; end; until( FileExists( fb.SelectedItem + '\bin\lcc.exe' ) );
finally fb.Free; end; |
Delphi-Laie - Di 01.04.08 01:45
So ganz begriffen habe ich es noch nicht - ist das nun eine Möglichkeit, ein Verzeichnis ("Ordner") oder eine Datei auszuwählen mit Wahlfreiheit? Wäre lässig, denn immerhin haben Dateien und Verzeichnisse bestimmte Eigenschaften gemeinsam, es gibt also durchaus Fälle, in denen man diese Wahlfreiheit haben möchte. Wenn das mit dieser Komponente funktionieren könnte, wäre ich begeistert.
Mathias, leider komme ich damit aber nicht klar. Es läuft bei mir einfach nicht, bis hinauf zum Delphi 7.
1. Du schreibst, daß man eine Varialbe fb : TFolderBrowser deklarieren muß. Habe ich nach Compilermeckerei auch getan. Er benötigte jedoch auch noch folgende Variablendefinition: hwndDlg:cardinal;
2. Im Ausdruck fb.SetRoot(CSIDL_STARTMENU); stoßen sich alle meine Delphi-Compiler an der Variablen (oder Konstanten?) CSIDL_STARTMENU. Liegt es daran, daß ich kein XP benutze, ist die nur unter XP definiert?
3. Auch nach Eröffnung eines ganz einfachen Projektes mit nur einem Button:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, fldbrows;
type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private public end;
var Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject); var fb : TFolderBrowser; hwndDlg:cardinal;
begin fb := TFolderBrowser.Create(hwndDlg,'Bitte wählen Sie einen Ordner'); if fb <> nil then try if fb.Execute then ShowMessage(Format('gewählter Ordner: "%s"',[fb.SelectedItem])); finally fb.Free; end; end;
end. |
habe ich keinen echten Erfolg: Delphi meckert zwar nicht, aber es tut sich bei dem Klick auf den Button auch nichts.
Was mache ich falsch?
Herzlichen Dank im voraus für Antworten!
Netter Gruß
Delphi-Laie
Delete - Mi 02.04.08 12:54
Delphi-Laie hat folgendes geschrieben: |
1. Du schreibst, daß man eine Varialbe fb : TFolderBrowser deklarieren muß. Habe ich nach Compilermeckerei auch getan. Er benötigte jedoch auch noch folgende Variablendefinition: hwndDlg:cardinal; |
Nee, du hast das kleine Rote im Eröffnungsbeitrag nicht gelesen. Bei VCL-Programmen kann/soll/muss der erste Parameter auf das eigene Fenster-Handle verweisen, also "self.Handle" oder "Application.Handle". Irgendwie muss der Dialog ja erfahren, zu welchem Fenster er gehört. Arbeitest du in einem VCL-Formular, dann kannst du "self." auch weglassen und nur "Handle" angeben.
Zitat: |
2. Im Ausdruck fb.SetRoot(CSIDL_STARTMENU); stoßen sich alle meine Delphi-Compiler an der Variablen (oder Konstanten?) CSIDL_STARTMENU. Liegt es daran, daß ich kein XP benutze, ist die nur unter XP definiert? |
Das hat mit Windows nichts zu tun. Wenn der Compiler bzgl der Konstanten meckert, dann hat in erster Linie Delphi ein Problem damit. Eine mögliche Ursache wäre, wenn du eine andere Unit eingebunden hast, in der diese Konstanten bereits definiert sind. Aber dazu müsste man deine Fehlermeldung sehen.
Delphi-Laie - Mi 02.04.08 23:36
Lieber Mathias, vielen, herzlichen Dank! Dem roten Kleingedruckten hatte ich tatsächlich nicht die Bedeutung beigemessen. Ich habe es jetzt endlich hinbekommen (somit erscheint auch die Compilermeckerei nicht mehr).
Was ich allerdings nicht verstehe: Es erscheint wieder "nur" der ein Verzeichnisauswahldialog. Weiter vorn stand doch: "kann man damit nur Ordner wählen oder auch Dateien? Wenn auch Dateien funktionieren, fände ich es praktisch, wenn man die Möglichkeit hätte, mehrere Dateien/Ordner auswählen zu können, also eine "Multiselect"-Property.
...
Ursprünglich diente der Dialog nur dazu einen Ordner auszuwählen. Warum ihn Microsoft so erweitert hat, dass man auch Dateien auswählöen kann, weiß der Geier."
Kann man damit nun ohne extra Mausklick Dateien und Ordner auswählen oder nicht? Falls ja, wie kann man das anstellen?
Begrenzt (!) weiß ich mir selbst zu helfen und fand die Eigenschaft showfiles, jedoch hilft es auch nicht, diese zuvor zu aktivieren:
Delphi-Quelltext
1: 2:
| fb.ShowFiles:=true; fb := TFolderBrowser.Create(hwndDlg,'Bitte wählen Sie einen Ordner'); |
Mache ich schon wieder etwas falsch?
Allmählich treibt mich dieses Delphi in den Wahnsinn. Wie schön war das doch gute alte Turbo-Pascal, das funktionierte wenigstens determiniert, dagegen ist die Programmierung heutzutage um etliche Größenordnungen mehr zum Erbrechen....Spaß macht es wirklich keinen mehr, ganz im Gegensatz, die innere Explosion ist inzwischen ein Dauerzustand. Auch mein nächstes Problem, ein Programm bedingt ins Systray zu minimieren, läßt mich bis zum Irrsinn verzweifeln (trotz aller Foren, nichts, aber auch rein gar nichts hilft, obwohl das schon etliche Male angsprochen wurde), aber das ist ein anderes Thema.
Würde mich sehr freuen, wenn Du mir noch einmal helfen könntest, bitte.
Danke und nette Grüße
Delphi-Laie
Postscriptum: Diese Forumssoftware läßt einen auch an die Decke springen. Binnen viel zu kurzer Zeit der scheinbaren Inaktivität, in Wirklichkeit ist man fleißig am Tippen, wird man automatisch ausgeloggt (frühestens nach 1/2-1h wäre das m.E. akzeptabel), mit der Folge, daß das Geschriebene flöten geht. Aber wenigstens aus
dem Schaden bin ich inzwischen schlau geworden.
Christian S. - Do 03.04.08 12:41
Delphi-Laie hat folgendes geschrieben: |
Delphi-Quelltext 1: 2:
| fb.ShowFiles:=true; fb := TFolderBrowser.Create(hwndDlg,'Bitte wählen Sie einen Ordner'); |
Mache ich schon wieder etwas falsch? |
Du setzt die Eigenschaft bevor Du überhaupt das Objekt erzeugst.
Delphi-Laie hat folgendes geschrieben: |
Allmählich treibt mich dieses Delphi in den Wahnsinn. Wie schön war das doch gute alte Turbo-Pascal, das funktionierte wenigstens determiniert, |
Bei Delphi auch, so lange man überlegt, in welcher Reihenfolge die Anweisungen Sinn machen :zwinker:
Delphi-Laie hat folgendes geschrieben: |
dagegen ist die Programmierung heutzutage um etliche Größenordnungen mehr zum Erbrechen.... |
Von Deinem vorherigen Quelltext ausgehend: Vielleicht bist Du zu schnell vorgegangen und hast ein paar Grundlagen ausgelassen, was sich später immer rächt.
Delphi-Laie hat folgendes geschrieben: |
Postscriptum: Diese Forumssoftware läßt einen auch an die Decke springen. |
Du scheinst leicht reizbar zu sein, wenn Du ständig an der Decke rumhängst, um dort innerlich zu explodieren :lol:
Delphi-Laie hat folgendes geschrieben: |
Binnen viel zu kurzer Zeit der scheinbaren Inaktivität, in Wirklichkeit ist man fleißig am Tippen, wird man automatisch ausgeloggt (frühestens nach 1/2-1h wäre das m.E. akzeptabel), mit der Folge, daß das Geschriebene flöten geht. Aber wenigstens aus dem Schaden bin ich inzwischen schlau geworden. |
Der Session-Timeout ist eine halbe Stunde. Gilt natürlich nur, falls zwischendurch nicht Deine Internetverbindung getrennt wird, sodass Du eine neue IP bekommst. Auf jeden Fall hilft es, Cookies zuzulassen. Solltest Du weiter Probleme habe, erstelle bitte einen Thread in der Sparte "Wünsche, Anregungen und Kritik". Danke! :-)
Delete - Do 03.04.08 14:17
Titel: Re: bug report
vsh hat folgendes geschrieben: |
dann kommt ein fehler. |
Die Präzision mit der du den Fehler beschreibst ist unübertroffen.
Delphi-Laie - Do 03.04.08 15:41
Hallo Christian, vielen Dank für Deine Antwort!
1. Jetzt funktioniert es, der Dialog ist genial - GENAU DAS hätte ich eigentlich vor Jahren benötigt, wußte aber gar nicht, daß sich so etwas überhaupt realisieren läßt. So hatte ich damals zwei getrennte Dialoge in mein Programm eingeführt - wie unlässig. Das werde ich als nächstes verbessern. Wieso sich einem Objekt allerdings eine Eigenschaft ohne Compiler- oder wenigstens Laufzeitfehler zuweisen läßt, wenn dieses noch gar nicht existiert, verschließt sich mir jedoch völlig.
2. Mit der Reihenfolge: "Erst probieren, dann scheitern, zum Schluß aus dem Fehler lernen (oder auch nicht) hast Du völlig recht: "Learning by doing", wie das so schön im Neu"deutschen" heißt. Aber Hand aufs Herz: Habt selbst Ihr Delphi-Gurus es wirklich immer anders gehandhabt? Wenn ich mich mit Grundlagen beschäftigen wollte, wüßte ich bei der Menge gar nicht, wo ich anfangen sollte. Natürlich lernt man auch (und gerade?) aus Fehlern. Nochmals: Du hast völlig recht, Deine Vermutung darfst Du ruhig zur Kritik erweitern, denn diese ist berechtigt.
3. Wenn Du das wegstecken kannst, wenn andere (diplomatisch ausgedrückt: Programme) Dir Deine mühsame Arbeit mir nichts Dir nichts zerstören (Zurückbutton im IE zaubert natürlich auch nichts wieder hervor), dann hast Du mir auch das voraus. Ich kann es nicht. Timeout 1/1 h? Gemessen hatte ich es nie (wie auch?), nur soviel: dann ist das m.E. ein zu kleiner Wert (dann war meine Schätzung auch zu knausrig). Mir sind schon eine ganze Reihe von Post und PMs deshalb durch die Lappen gegangen (das erste Mal wäre ich auch noch gelassen gewesen) - diesbezüglich bin ich also inzwischen ein ziemlich gebranntes Kind.
Nochmals vielen, herzlichen Dank!
Netter Gruß
Delphi-Laie
Delphi-Laie - Mo 09.02.09 15:02
Hallo, melde mich nach längerer Zeit einmal wieder.
Eine an die Programmierer dieser hervorragenden Komponente gerichtetes Anliegen: "TFolderBrowser" scheint ein "irgendwie" modifizierter Verzeichnisauswahldialog von Windows zu sein, soviel ist mir klar (Hut ab vor diesen fast schon Zauberkenntnissen!). Ist das doch recht kleine Auswahlfenster evtl. sogar noch zoombar wie bei Dateiauswahldialogen (ab Delphi 4 sind solche auch implementiert)? Ich fürchte, nein, weil Verzeichnisauswahldialoge es auch nicht sind, möchte es aber dennoch zur Sicherheit vorsichtig fragen. Das ist das einzige, was ich daran noch zu verbessern mir vorstelle.
Vielen Dank und viele Grüße
Delphi-Laie
jaenicke - Mo 09.02.09 16:01
Delphi-Laie hat folgendes geschrieben : |
"TFolderBrowser" scheint ein "irgendwie" modifizierter Verzeichnisauswahldialog von Windows zu sein |
Das ist der normale eingebaute. ;-)
Der wird mit der API-Funktion
SHBrowseForFolder [
http://msdn.microsoft.com/en-us/library/bb762115.aspx] angezeigt.
Delphi-Laie hat folgendes geschrieben : |
Ist das doch recht kleine Auswahlfenster evtl. sogar noch zoombar wie bei Dateiauswahldialogen |
Laut
Dokumentation [
http://msdn.microsoft.com/en-us/library/bb762115.aspx] reicht dafür das Flag
BIF_USENEWUI. Einfach irgendwo bei der Initialisierung der Flags (ca. Zeile 370) diese Zeile einfügen:
Delphi-Quelltext
1:
| BrowseInfo.ulFlags := BrowseInfo.ulFlags or BIF_USENEWUI; |
Warum das wohl nicht drin ist, steht auch als Kommentar dabei:
Es kann also unerwünschte Nebeneffekte geben.
Delphi-Laie - Di 10.02.09 18:33
Tausend Dank, habe tatsächlich den gewünschten Erfolg erreicht! Nein, das hätte ich selbst, wenn ich mich durch Mikroweichs Originaldokumentation gekämpft hätte, nicht herausbekommen, z.B. schon allein deshalb, weil dort die Wörter "zoom" oder "zoomable" nirgendwo auftauchen.
Was Luckie mit "Button immer angezeigt" und Du mit "unerwünschten Nebeneffekten" mein(s)t, ist mir nicht ganz klar: Das zoombare Fenster unterscheidet sich von seinem einfacheren Pendant dadurch, daß es unten noch ein Label "Ordner:" und ein Editfenster hat, das das angeklickte Ergebnis anzeigt, von dem allerdings manuelle Eingaben nicht weitergeleitet bzw. verarbeitet werden; Originalton bei MS:
Zitat: |
...including an edit box |
Zur Zeit ist hier übrigens die Version mit Copyright 2003-2005 downloadbar, vor geraumer Zeit lud ich jedoch eine Unit-Datei mit Copyright 2003-2006 herunter (die ich zur Zeit verwende). Weißt Du, ob etwas jüngeres herausgenommen und etwas älteres wieder hochgeladen wurde?
Nochmals vielen Dank und freundlicher Gruß
Delphi-Laie
jaenicke - Di 10.02.09 19:07
Delphi-Laie hat folgendes geschrieben : |
z.B. schon allein deshalb, weil dort die Wörter "zoom" oder "zoomable" nirgendwo auftauchen. |
Zoom ist ja auch etwas ganz anderes als das was du meinst. Du meinst resizeable (Fenster in der Größe veränderbar).
Zoom bedeutet den
Inhalt eines Fensters in der Größe zu verändern. ;-)
Delphi-Laie - Di 10.02.09 21:34
OK, danke!
Ich staune immer wieder, wie sehr ich meinem Pseudonym alle Ehre mache. Und das, obwohl ich seit 1989 Pascal und seit 1990 (oder 1991) Turbo-Pascal kenne und damit auch programmiere. Delphi und Windows sind eben doch mehr als großes Pascal und DOS. Nunmehr ist es relativ einfach, und so fand ich heraus, daß es auch mit
Delphi-Quelltext
1:
| BrowseInfo.ulFlags := BrowseInfo.ulFlags or BIF_NEWDIALOGSTYLE; |
möglich ist (und nur bei diesem Flag taucht bei MSDN das Wort "resized" überhaupt auf!), und zwar ohne dieses m.E. nahezu überflüssige (und deshalb in meinen Augen platzverschwendende und mithin ätzende) Editfeld. (naja, man kann es sich erschließen, weil auch steht: "BIF_USENEWUI [...] the new user interface, including an edit box. This flag is equivalent to BIF_EDITBOX | BIF_NEWDIALOGSTYLE.")
Yippie, nochmals allerbesten Dank!!
jaenicke - Di 10.02.09 21:49
Delphi-Laie hat folgendes geschrieben : |
und zwar ohne dieses m.E. nahezu überflüssige (und deshalb in meinen Augen platzverschwendende und mithin ätzende) Editfeld. |
So unterschiedlich sind dazu die Meinungen. Ich ärgere mich jedesmal wieder, wenn eine Software es nicht anzeigt.
Denn so darf ich jedesmal wieder ggf. mich durch die Ordner klicken anstatt einfach einen Pfad zu kopieren und immer wieder komplett dort einzufügen...
Wenn ich das dann mehrfach machen muss, dann kostet das enorm viel Zeit.
Zudem werden, wenn ich anfange dort zu tippen, auch alle passenden Ordner zur Auswahl angezeigt, ich kann also blitzschnell auch den richtigen Ordner finden. Insgesamt komme ich jedenfalls mit dem Editfeld oft sehr viel schneller zum Ziel...
Delphi-Laie - Di 10.02.09 22:03
Das ist natürlich ein gutes Argument. Nur, bei mir funktioniert das nicht. Sind z.B. die eigenen Dateien oder Arbeitsplatz aktiviert und gebe ich im Editfeld c: ein und drücke danach Enter oder auf den OK-Knopf, lande ich trotzdem an der Stelle, die weiter oben, durch Andersfärbung angezeigt, aktiv(iert) war. Deshalb konnte ich dem Editfeld ja auch nichts abringen (s.o.).
Hast wirklich Geduld mit mir, vielen Dank!
Edit: OK, wenn ich "C:\" (also mit Backslash) in das Editfeld eingebe, entfaltet es Wirkung, und was ich dann von den erscheinenden Vorschlägen auswähle, wird auch übernommen. Funktioniert sogar z.B. auch mit "Arbeitsplatz\" und bzw. oder "Desktop\".
jaenicke - Mo 13.04.09 15:37
Ich habe einmal das für Delphi 2009 ergänzt, viel war ja nicht zu ändern. Die entsprechenden Abschnitte sind mit {$ifdef UNICODE} entsprechend unterschieden. Die Unit sollte mit älteren Versionen also genauso funktionieren wie vorher.
Gausi - Mo 13.04.09 16:37
Hey, das kommt für mich gerade richtig. :D
Eine Frage habe ich aber zu dem Flag
BIF_USENEWUI. Im Code steht der Kommentar
Da du dich jetzt etwas damit beschäftigt hast - was ist damit gemeint, und/oder ist das unter XP noch aktuell? Denn wenn man die Flags mit
BrowseInfo.ulFlags := BIF_USENEWUI; initialisiert (also wohl das rausgenommene wieder reinnimmt), dann sieht der Dialog doch etwas besser aus (Größe veränderbar und mit Editfeld zur Direkteingabe des Ordners), und andere negative Effekte kann ich auf Anhieb nicht beobachten.
Edit: Ok, die vorigen Beiträge nicht alle gelesen - aber ich sehe da trotzdem nichts negatives dran. :nixweiss:
jaenicke - Mo 13.04.09 16:50
Das sehe ich genauso (@Edit), ich habe das bei mir auch drin (aber auch ein bisschen was anderes verändert). Nur hier wollte ich jetzt die Originalversion nur für Delphi 2009 verändert anhängen. ;-)
n-iceman - Mi 22.04.09 16:49
Danke erstmal an alle die an der fldbrows.pas gearbeitet haben, echt genial!
Trotzdem kurz ein Frage:
Wie kann ich zwar den Root Folder angeben aber einen speziellen Ordner bereits vorselektieren (vorschlagen)?
Hintergrund:
Der Benutzer soll im gesamten Arbeitsplatz etwas Auswählen können, ich möchte aber das bereits ein Ziel vorgeschlagen wird.
Wenn dies dem Benutzer nicht passt, kann er selber browsen und ein Ziel angeben, ansonsten den Vorschlag übernehmen und mit "OK" weiter gehen.
Danke bereits für weitere Infos.
Th69 - Mi 22.04.09 17:06
Einfach als dritten Parameter im Konstruktor angeben, s. a. den 1. Beitrag in diesem Thread -)
P.S. Für einen einfachen BrowseForFolder-Aufruf kann man auch die VCL-Funktion SelectDirectory verwenden:
http://delphi.about.com/library/rtl/blrtlSelectDirectory.htm
Dort dann einfach die Variable 'Dir' mit einem Verzeichnis vorbelegen...
n-iceman - Do 23.04.09 00:33
Danke für die schnelle Antwort, aber meine Frage bezieht sich nicht auf das Rootverzeichnis, sondern darauf, das ein Ziel vorselektiert werden soll.
Beispiel:
Der Benutzer soll einen künftigen Installationspfad einer Applikation wählen.
Als Root wird der gesamte Arbeitsplatz angegeben (Der Benutzer kann also auf C:\test oder C:\Users usw auswählen.)
Diese Freiheit möchte ich beibehalten, aber einen Vorschlag vorselektieren.
--> Bsp. C:\ProgramFiles\...
Danlke für weitere Hilfe.
Th69 - Do 23.04.09 09:36
Genau das habe ich dir geschrieben. Hast du es denn mal ausprobiert?
Zitat: |
Die Create-Methode hat noch mehr Parameter, die aber nicht zwangsläufig benutzt werden müssen, wie man ja auch sehen kann. Soll bspw. ein bestimmter Ordner vorausgewählt sein, wird er einfach als dritter Parameter angehangen,
|
Delphi-Quelltext
1: 2: 3:
| fb := TFolderBrowser.Create(hwndDlg, 'Bitte wählen Sie einen Ordner', 'c:\windows'); |
Und das Root-Verzeichnis kann man noch explizit mit SetRoot(...) setzen.
Regan - Do 23.04.09 11:25
Th69 hat folgendes geschrieben : |
Genau das habe ich dir geschrieben. Hast du es denn mal ausprobiert?
Zitat: |
Die Create-Methode hat noch mehr Parameter, die aber nicht zwangsläufig benutzt werden müssen, wie man ja auch sehen kann. Soll bspw. ein bestimmter Ordner vorausgewählt sein, wird er einfach als dritter Parameter angehangen,
|
Delphi-Quelltext 1: 2: 3:
| fb := TFolderBrowser.Create(hwndDlg, 'Bitte wählen Sie einen Ordner', 'c:\windows'); |
Und das Root-Verzeichnis kann man noch explizit mit SetRoot(...) setzen. |
Nein, das meint er aber nicht. Sonder er möchte, dass der Nutzer keine Ebene weiter hoch gehen kann. Also alles in ProgramFiles bleibt aber dort dann beliebig wählen kann.
Das geht mit dieser Komponente meines Erachtens nicht.
n-iceman - Do 23.04.09 12:52
Hallo zusammen,
Th69 hatte absolut Recht!
Genauso wollte ich es haben, merci.
Ich hatte den dritten (Von th69 genannten) Parameter mit SetRoot verwechselt.... :roll:
@Th69
Danke nochmals, in der Hitze des Gefechtes habe ich das doch glatt missverstanden.
Gruess
trm - So 13.12.09 00:09
Huhu.
Ich habe die unit schon seit etlichen Jahren im Einsatz.
Seit heute gibt es eigenartige Fehlermeldungen, die ich nicht nachvollziehen kann, da nichts geändert wurde:
[Fehler] fldbrows.pas(369): Inkompatible Typen: 'Array' und 'Char'
---> BrowseInfo.pszDisplayName := @Displayname;
[Fehler] fldbrows.pas(371): Inkompatible Typen: 'TFNBFFCallBack' und 'Pointer'
---> BrowseInfo.lpfn := @FolderCallBack;
[Fataler Fehler] Unit1.pas(11): Verwendete Unit 'fldbrows.pas' kann nicht compiliert werden
Kann das jemand nachvollziehen oder mir erklären, wo der Fehler steckt ?
Ich nutze Delphi7.
Gruß + schönen 3. Advent :)
~Mathias (TRM aus *EX* Spotlight)
Edit:
Kurios:
Ich habe jetzt mal testweise ein anderes Projekt geschnappt, in der die gleiche Unit eingebunden ist.
Dort tritt der Fehler nicht auf.
Außerdem: Sobald ich in dem Projekt, in dem der Fehler auftritt, die Pointerdeklarationen entferne (das @), dann gibt es keinerlei Fehlermeldungen und auch keinerlei Warnhinweise, jedoch funktioniert die Kompilierung tadellos. Auch die Unit funktioniert komischerweise genauso wie früher, bevor der Fehler auftrat.
Kann sich jemand darauf einen Reim machen?
Noch ein Hinweis:
Solange ich die erste Fehlermeldung anders parse, erst über einen String, dann über einen PChar (pszDisplayName := PChar(String(@DisplayName)); ), dann gibt es auch keine Fehlermeldung mehr.
Wieder die Frage:
Wie kommt sowas zustande?
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!