Autor Beitrag
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Fr 02.07.04 22:09 
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-Beitrag von Peter Lustig, entstand in diesem Beitrag die Version im Anhang.

Hier das Beispiel für den normalen Aufruf:
ausblenden 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,
ausblenden Delphi-Quelltext
1:
2:
3:
fb := TFolderBrowser.Create(hwndDlg,
  'Bitte wählen Sie einen Ordner',
  'c:\windows');


Moderiert von user profile iconMathiasSimmack: "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:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
if(fb <> nilthen
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
ausblenden 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
ausblenden 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:
ausblenden Delphi-Quelltext
1:
  fb.Filter := '*.txt';					

mehrere Filter müssen durch das #0-Zeichen voneinander getrennt werden:
ausblenden 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
ausblenden 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 ;):
ausblenden 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 <> nilthen
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.
Einloggen, um Attachments anzusehen!


Zuletzt bearbeitet von MathiasSimmack am So 25.09.05 10:30, insgesamt 6-mal bearbeitet
Nightmare_82
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 260



BeitragVerfasst: 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.
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 295



BeitragVerfasst: Sa 14.08.04 21:36 
Was genau macht dieser Code?

ausblenden 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:

ausblenden Quelltext
1:
fb := TFolderBrowser.Create(hwndDlg);					


Der Construkter mit dieser Signatur existiert nicht ... es muss noch mind. ein String als Parameter übergeben werden.
:D
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Sa 14.08.04 23:42 
hitstec hat folgendes geschrieben:
Was genau macht dieser Code?

Suche im MSDN "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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 295



BeitragVerfasst: 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

ausblenden Quelltext
1:
if (CoInitialize(nil)=S_OK) then					


lieber

ausblenden Quelltext
1:
 if not (CoInitializeEx(nil,irgendeinekonstante)=S_FALSE) then					


verwenden.

Gruß
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: 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
ausblenden Delphi-Quelltext
1:
if not(CoInitializeEx(nil,...) = S_FALSE)					

wohl eher
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 295



BeitragVerfasst: Mi 18.08.04 09:36 
Oki! :P
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mi 18.08.04 16:20 
Okay, ich hab´s jetzt wie folgt geändert:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
  hr           := CoInitializeEx(nil,COINIT_APARTMENTTHREADED);
  // Wenn die COM-Bibliothek noch nicht initialisiert ist,
  // dann ist das Ergebnis S_OK; ist sie bereits initialisiert
  // ist sie S_FALSE
  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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 295



BeitragVerfasst: Mi 18.08.04 18:42 
So ists recht. Mehr habe ich nicht mehr hinzuzufügen! :o
MarkusZi
Hält's aus hier
Beiträge: 11

Win XP, Vista
D4 Prof
BeitragVerfasst: 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.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 20451
Erhaltene Danke: 2264

Win 10
C# (VS 2019)
BeitragVerfasst: 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

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".


Zuletzt bearbeitet von Christian S. am So 05.06.05 17:49, insgesamt 1-mal bearbeitet
MarkusZi
Hält's aus hier
Beiträge: 11

Win XP, Vista
D4 Prof
BeitragVerfasst: Sa 04.06.05 18:31 
Super, Danke zetzt geht´s !
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: 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:
ausblenden Delphi-Quelltext
1:
2:
uses
  { eure sonstigen Units }, fldbrows;


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:
ausblenden 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.
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: 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):
ausblenden 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 <> nilthen
try
  fb.SetRoot(CSIDL_COMMON_STARTMENU);
  fb.ShowFiles := true;
  fb.NoTargetTranslation := true;

  if(fb.Execute) then
  begin
    // hier wird geprüft, ob es sich um eine MSI-Verknüpfung handelt,
    // die man zurückverfolgen kann, ...
    tmp := fb.TranslateMsiLink(fb.SelectedItem);

    // ... Wenn Nein, dann ist der String leer, und es wird die bisher
    // bekannte "TranslateLink"-Funktion verwendet
    if(tmp = ''then tmp := fb.TranslateLink(fb.SelectedItem);

    // Ergebnis anzeigen
    MessageBox(hwndDlg, pchar(fb.SelectedItem + ' -> ' + tmp),
      'Verknüpfungsziel', MB_OK or MB_ICONINFORMATION)
  end;
finally
  FreeAndNil(fb);
end;
ash0r
Hält's aus hier
Beiträge: 2



BeitragVerfasst: 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 ..
Einloggen, um Attachments anzusehen!
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: 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
ausblenden Delphi-Quelltext
1:
{.$DEFINE VCLCOMPONENTMODE}					

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:
ausblenden 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.
Einloggen, um Attachments anzusehen!
ash0r
Hält's aus hier
Beiträge: 2



BeitragVerfasst: Fr 10.03.06 16:45 
hast recht , ich musste diese funktion etwas aendern:
ausblenden 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:
ausblenden 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 user profile iconChristian S.: Code- durch Delphi-Tags ersetzt
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Sa 11.03.06 10:44 
user profile iconash0r hat folgendes geschrieben:
hast recht , ich musste diese funktion etwas aendern:
ausblenden 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:
ausblenden 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
Hält's aus hier
Beiträge: 1



BeitragVerfasst: 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