Unit containing its own form lazarus / 21-General ---------------------------------------------------------------------- torbente · 2021-01-26 08:42 I coded a simple file explorer (image included) Then i try to make an unit from it. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit FormExplore; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, forms, controls, stdctrls,Graphics, buttons, grids, fileutil; function OnlyName(conpath:string):string;procedure FGridFilesDblClick(Sender: TObject);Procedure LoadDirectory(directory:string);procedure FSBUpPathClick(Sender: TObject);Procedure ShowExplorer(Directory:string); var  FExplorer : TForm;  EditPath : Tedit;  FSBUpPath : TSpeedButton;  FGridFiles : TStringGrid;  FEditFilename : Tedit;  FSBOkFile : TSpeedButton;   FActiveDir : String = '';  FilesDir : TStringList;  FoldersDir : TStringList;  Result : String = ''; implementation Procedure ShowExplorer(Directory: String);BeginLoadDirectory(Directory);FExplorer.Visible:=true;End; fun ction OnlyName(conpath:string):string;var  cont : integer;Beginfor cont := length(conpath) downto 1 do   if conpath[cont] = '\' then    begin      result := copy(conpath,cont+1,length(conpath));      break;      end;end; procedure FSBUpPathClick(Sender: TObject);var  contador : integer;beginfor contador := length(FActiveDir) downto 1 do   begin   if FActiveDir[contador] = '\' then      begin      FActiveDir := copy(FActiveDir,1,contador-1);      LoadDirectory(FActiveDir);      break      end;   end;end; procedure FGridFilesDblClick(Sender: TObject);beginif FGridFiles.Row>0 then   begin   if copy(FGridFiles.Cells[0,FGridFiles.Row],1,3) = '   ' then      begin      FActiveDir := FActiveDir+'\'+ copy(FGridFiles.Cells[0,F GridFiles.Row],4,length(FGridFiles.Cells[0,FGridFiles.Row]));      LoadDirectory(FActiveDir);      end;   end;end; procedure FGridFilesSelection(Sender: TObject; aCol, aRow: Integer);beginFEditFi lename.Text:=FGridFiles.Cells[0,FGridFiles.Row];if copy(FEditFilename.Text,1,3)='   ' then FEditFilename.Text:='';end; Procedure LoadDirectory(directory:string);var  cont : integer;BeginFilesDir.Clea r;FoldersDir.clear;FGridFiles.RowCount:=1;FindAllFiles(FilesDir, directory, '*.*', false);FoldersDir := FindAllDirectories(directory,false);if FoldersDir.Count>0 then   Begin   for cont := 0 to FoldersDir.Count-1 do      begin      FGridFiles.RowCount:=FGridFiles.RowCount+1;      FGridFiles.Cells[0,cont+1]:='   '+OnlyName(FoldersDir[cont]);      end;   end;if FilesDir.Count>0 then   begin   for cont := 0 to FilesDir.Count-1 do      begin      FGridFiles.RowCount:=FGridFiles.RowCount+1;      FGridFiles.Cells[0,FGridFiles.RowCount-1]:=OnlyName(FilesDir[cont]);    end;   end;EditPath.Text:=directory;End; beginFExplorer := TForm.Create(nil);FExplorer.caption := 'File Explorer';FExplorer.SetBounds(0, 0, 450, 350);FExplorer.BorderStyle := bssingle;FExplorer.Position:=poOwnerFormCenter;FExplorer.BorderIcons:= FExplorer.BorderIcons-[biminimize]; EditPath := TEdit.Create(FExplorer );EditPath.Parent:=FExplorer;EditPath.AutoSize:=false;EditPath.Left:=1 ;EditPath.Top:=1;EditPath.Height:=24;EditPath.Width:=424;EditPath.Font .Name:='consolas'; EditPath.Font.Size:=12;EditPath.Color:=clBlack;Edit Path.Font.color:=clwhite;EditPath.Alignment:=taLeftJustify;EditPath.Vi sible:=true;EditPath.Enabled:=true;EditPath.ReadOnly:=true; FSBUpPath := TSpeedButton.Create(FExplorer);FSBUpPath.Parent:=FExplorer;FSBUpPat h.Left:=426;FSBUpPath.Top:=2;FSBUpPath.Height:=22;FSBUpPath.Width:=22; FSBUpPath.Visible:=true;FSBUpPath.hint:='Go up';FSBUpPath.ShowHint:=tr ue;//FSBUpPath.OnClick:=SBUpPathClick; FGridFiles := TStringGrid.Creat e(nil);FGridFiles.Parent:=FExplorer;FGridFiles.Font.Name:='consolas'; FGridFiles.Font.Size:=12;FGridFiles.Left:=2;FGridFiles.Top:=24;FGridFi les.Height:=298;FGridFiles.width:=446;FGridFiles.FixedCols:=0;FGridFil es.FixedRows:=1;FGridFiles.rowcount := 1;FGridFiles.ColCount:=4;FGridF iles.ScrollBars:=ssAutoVertical;FGridFiles.Options:= FGridFiles.Options-[goRangeSelect];FGridFiles.ColWidths[0]:= 444;FGridFiles.Cells[0,0]:= 'Name';FGridFiles.FocusRectVisible:=false; //FGridFiles.OnDblClick:=FGridFilesDblClick;//FGridFiles.OnSelection:= FGridFilesSelection; FEditFilename := TEdit.Create(FExplorer);FEditFil ename.Parent:=FExplorer;FEditFilename.AutoSize:=false;FEditFilename.Le ft:=1;FEditFilename.Top:=324;FEditFilename.Height:=24;FEditFilename.Wi dth:=384;FEditFilename.Font.Name:='consolas'; FEditFilename.Font.Size: =12;FEditFilename.Color:=clBlack;FEditFilename.Font.color:=clwhite;FEd itFilename.Alignment:=taLeftJustify;FEditFilename.Visible:=true;FEditF ilename.Enabled:=true;FEditFilename.ReadOnly:=true; FSBOkFile := TSpee dButton.Create(FExplorer);FSBOkFile.Parent:=FExplorer;FSBOkFile.Left:= 388;FSBOkFile.Top:=324;FSBOkFile.Height:=22;FSBOkFile.Width:=60;FSBOkF ile.Visible:=true;FSBOkFile.hint:='Accept';FSBOkFile.ShowHint:=true;// FSBOkFile.OnClick:=@form1.FSBOkFileOnClick; FilesDir := TStringList.Create;FoldersDir := TStringList.Create;END.   But im missing something since im unable to assign the event functions to its controls. (lines 129,141,142,155) PS: The unit is still not finished, but im stuck here. It actually works but without control events, it is impossible to navigate. ---------------------------------------------------------------------- dseligo · 2021-01-26 09:23 I think that event procedures should be part of class (method). So, try to create a class and put this procedures in that class. ---------------------------------------------------------------------- howardpc · 2021-01-26 10:05 something like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit FormExplore; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, FileUtil,  Forms, Controls, StdCtrls, Graphics, Buttons, Grids; type  TExplorerForm = class(TForm)  private    EditPath: TEdit;    FActiveDir: String;    FEditFilename: TEdit;    FGridFiles: TStringGrid;    FilesDir: TStringList;    FoldersDir: TStringList;    FSBOkFile: TSpeedButton;    FSBUpPath: TSpeedButton;      procedure FSBOkFileOnClick(Sender: TObject);    function OnlyName(const conpath: String): String;    procedure FGridFilesDblClick(Sender: TObject);    procedure FGridFilesSelection(Sender: TObject; aCol, aRow: Integer);  procedure SBUpPathClick(Sender: TObject);    procedure LoadDirectory(const Directory: String);  public    constructor Create(TheOwner: TComponent); override;    destructor Destroy; override;  end;   procedure ShowExplorer(const Directory: String); implementation var  explorer: TExplorerForm = Nil; procedure ShowExplorer(const Directory: String); begin  if not Assigned(explorer) then    explorer := TExplorerForm.Create(Nil);  explorer.LoadDirectory(Directory);  explorer.Show;end; { TExplorerForm } constructor TExplorerForm.Create(TheOwner: TComponent);begin  inherited CreateNew(TheOwner);  Caption := 'File Explorer';  SetBounds(0, 0, 450, 350);  BorderStyle := bssingle;  Position := poOwnerFormCenter;  BorderIcons := BorderIcons - [biminimize];   FilesDir := TStringList.Create;  FoldersDir := TStringList.Create;   EditPath := TEdit.Create(Self);  with EditPath do    begin      SetBounds(1, 1, 424, 24);      Font.Name := 'consolas';      Font.Size := 12;      Font.Color := clWhite;      Color := clBlack;      Alignment := taLeftJustify;      ReadOnly := True;      Parent := Self;    end;   FSBUpPath := TSpeedButton.Create(Self);  with FSBUpPath do    begin      SetBounds(426, 2, 22, 22);      Hint := 'Go up';      ShowHint := True;      OnClick := @SBUpPathClick;      Parent := Self;    end;   FGridFiles := TStringGrid.Create(Self);  with FGridFiles do    begin      SetBounds(2, 24, 446, 298);      Font.Name := 'consolas';      Font.Size := 12;      FixedCols := 0;    FixedRows := 1;      RowCount := 1;      ColCount := 4;      ScrollBars := ssAutoVertical;      Options := Options - [goRangeSelect];      ColWidths[0] := 444;      Cells[0,0] := 'Name';      FocusRectVisible := False;      OnDblClick := @FGridFilesDblClick;      OnSelection := @FGridFilesSelection;      Parent := Self;    end;   FEditFilename := TEdit.Create(Self);  with FEditFilename do    begin      SetBounds(1, 324, 384, 24);      AutoSize := False;      Font.Name := 'consolas';      Font.Size := 12;      Font.Color := clWhite;      Color := clBlack;      Alignment := taLeftJustify;      ReadOnly := True;      Parent := Self;    end;   FSBOkFile := TSpeedButton.Create(Self);  with FSBOkFile do    begin      SetBounds(388, 324, 60, 22);      ShowHint := True;      Hint := 'Accept';      OnClick := @FSBOkFileOnClick;      Parent := Self;    end; end; destructor TExplorerForm.Destroy;begin  FilesDir.Free;  FoldersDir.Free;  inherited Destroy;end; procedure TExplorerForm.FGridFilesDblClick(Sender: TObject);begin  if (FGridFiles.Row > 0) and (Copy(FGridFiles.Cells[0,FGridFiles.Row],1,3) = '   ' ) then    begin      FActiveDir := FActiveDir+'\'+ Copy(FGridF iles.Cells[0,FGridFiles.Row],4,Length(FGridFiles.Cells[0,FGridFiles.Ro w]));      LoadDirectory(FActiveDir);   end;end; procedure TExplorerForm.FGridFilesSelection(Sender: TObject; aCol, aRow: Integer);begin  FEditFilename.Text := FGridFiles.Cells[0,FGridFiles.Row];  if Copy(FEditFilename.Text,1,3) = '   ' then    FEditFilename.Text := '';end; procedure TExplorerForm.FSBOkFileOnClick(Sender: TObject);begin  // your code hereend; procedure TExplorerForm.LoadDirectory(const Directory: String);var  cont: Integer;begin  FilesDir.Clear;  FoldersDir.Clear;  FGridFiles.RowCount := 1;  FindAllFiles(FilesDir, Directory, '*.*', False);  FindAllDirectories(FoldersDir, Directory, False);  if FoldersDir.Count > 0 then    for cont := 0 to FoldersDir.Count-1 do    begin        FGridFiles.RowCount := FGridFiles.RowCount+1;        FGridFiles.Cells[0,cont+1] := '   ' + OnlyName(FoldersDir[cont]);    end;  if FilesDir.Count > 0 then    for cont := 0 to FilesDir.Count-1 do      begin        FGridFiles.RowCount := FGridFiles.RowCount+1;      FGridFiles.Cells[0,FGridFiles.RowCount-1] := OnlyName(FilesDir[cont]);      end;  EditPath.Text := Directory;end; function TExplorerForm.OnlyName(const conpath: String): String;var  cont: Integer;begin  for cont := Length(conpath) downto 1 do   if conpath[cont] = '\' then     begin       Result := Copy(conpath, cont+1, Length(conpath));       Break;     end;end; procedure TExplorerForm.SBUpPathClick(Sender: TObject);var  contador : integer;beginfor contador := length(FActiveDir) downto 1 do   begin   if FActiveDir[contador] = '\' then      begin        FActiveDir := copy(FActiveDir,1,contador-1);        LoadDirectory(FActiveDir);        Break;      end;   end;end; finalization   explorer.Free;  explorer := Nil; end. ---------------------------------------------------------------------- torbente · 2021-01-26 13:26 Your code works perfect! Thanks a lot. I did not have idea i need declare a new class for the form. Actually, when ok button is pressed, it returns the full path showed in the upper Tedit+'\'+Name of the file specified. The string is exactly the same i got from the windows file explorer. But if i apply a Fileexists to this string, i get a false  . I guest it is something with the application dir, but it not supposed that with the complete path it should work? I try changing the directory separator from '\' to '/' but it did not work  neither Quote from: howardpc on January 26, 2021, 11:05:32 amsomething like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit FormExplore; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, FileUtil,  Forms, Controls, StdCtrls, Graphics, Buttons, Grids; type  TExplorerForm = class(TForm)  private    EditPath: TEdit;    FActiveDir: String;    FEditFilename: TEdit;    FGridFiles: TStringGrid;    FilesDir: TStringList;    FoldersDir: TStringList;    FSBOkFile: TSpeedButton;    FSBUpPath: TSpeedButton;      procedure FSBOkFileOnClick(Sender: TObject);    function OnlyName(const conpath: String): String;    procedure FGridFilesDblClick(Sender: TObject);    procedure FGridFilesSelection(Sender: TObject; aCol, aRow: Integer);  procedure SBUpPathClick(Sender: TObject);    procedure LoadDirectory(const Directory: String);  public    constructor Create(TheOwner: TComponent); override;    destructor Destroy; override;  end;   procedure ShowExplorer(const Directory: String); implementation var  explorer: TExplorerForm = Nil; procedure ShowExplorer(const Directory: String); begin  if not Assigned(explorer) then    explorer := TExplorerForm.Create(Nil);  explorer.LoadDirectory(Directory);  explorer.Show;end; { TExplorerForm } constructor TExplorerForm.Create(TheOwner: TComponent);begin  inherited CreateNew(TheOwner);  Caption := 'File Explorer';  SetBounds(0, 0, 450, 350);  BorderStyle := bssingle;  Position := poOwnerFormCenter;  BorderIcons := BorderIcons - [biminimize];   FilesDir := TStringList.Create;  FoldersDir := TStringList.Create;   EditPath := TEdit.Create(Self);  with EditPath do    begin      SetBounds(1, 1, 424, 24);      Font.Name := 'consolas';      Font.Size := 12;      Font.Color := clWhite;      Color := clBlack;      Alignment := taLeftJustify;      ReadOnly := True;      Parent := Self;    end;   FSBUpPath := TSpeedButton.Create(Self);  with FSBUpPath do    begin      SetBounds(426, 2, 22, 22);      Hint := 'Go up';      ShowHint := True;      OnClick := @SBUpPathClick;      Parent := Self;    end;   FGridFiles := TStringGrid.Create(Self);  with FGridFiles do    begin      SetBounds(2, 24, 446, 298);      Font.Name := 'consolas';      Font.Size := 12;      FixedCols := 0;    FixedRows := 1;      RowCount := 1;      ColCount := 4;      ScrollBars := ssAutoVertical;      Options := Options - [goRangeSelect];      ColWidths[0] := 444;      Cells[0,0] := 'Name';      FocusRectVisible := False;      OnDblClick := @FGridFilesDblClick;      OnSelection := @FGridFilesSelection;      Parent := Self;    end;   FEditFilename := TEdit.Create(Self);  with FEditFilename do    begin      SetBounds(1, 324, 384, 24);      AutoSize := False;      Font.Name := 'consolas';      Font.Size := 12;      Font.Color := clWhite;      Color := clBlack;      Alignment := taLeftJustify;      ReadOnly := True;      Parent := Self;    end;   FSBOkFile := TSpeedButton.Create(Self);  with FSBOkFile do    begin      SetBounds(388, 324, 60, 22);      ShowHint := True;      Hint := 'Accept';      OnClick := @FSBOkFileOnClick;      Parent := Self;    end; end; destructor TExplorerForm.Destroy;begin  FilesDir.Free;  FoldersDir.Free;  inherited Destroy;end; procedure TExplorerForm.FGridFilesDblClick(Sender: TObject);begin  if (FGridFiles.Row > 0) and (Copy(FGridFiles.Cells[0,FGridFiles.Row],1,3) = '   ' ) then    begin      FActiveDir := FActiveDir+'\'+ Copy(FGridF iles.Cells[0,FGridFiles.Row],4,Length(FGridFiles.Cells[0,FGridFiles.Ro w]));      LoadDirectory(FActiveDir);   end;end; procedure TExplorerForm.FGridFilesSelection(Sender: TObject; aCol, aRow: Integer);begin  FEditFilename.Text := FGridFiles.Cells[0,FGridFiles.Row];  if Copy(FEditFilename.Text,1,3) = '   ' then    FEditFilename.Text := '';end; procedure TExplorerForm.FSBOkFileOnClick(Sender: TObject);begin  // your code hereend; procedure TExplorerForm.LoadDirectory(const Directory: String);var  cont: Integer;begin  FilesDir.Clear;  FoldersDir.Clear;  FGridFiles.RowCount := 1;  FindAllFiles(FilesDir, Directory, '*.*', False);  FindAllDirectories(FoldersDir, Directory, False);  if FoldersDir.Count > 0 then    for cont := 0 to FoldersDir.Count-1 do    begin        FGridFiles.RowCount := FGridFiles.RowCount+1;        FGridFiles.Cells[0,cont+1] := '   ' + OnlyName(FoldersDir[cont]);    end;  if FilesDir.Count > 0 then    for cont := 0 to FilesDir.Count-1 do      begin        FGridFiles.RowCount := FGridFiles.RowCount+1;      FGridFiles.Cells[0,FGridFiles.RowCount-1] := OnlyName(FilesDir[cont]);      end;  EditPath.Text := Directory;end; function TExplorerForm.OnlyName(const conpath: String): String;var  cont: Integer;begin  for cont := Length(conpath) downto 1 do   if conpath[cont] = '\' then     begin       Result := Copy(conpath, cont+1, Length(conpath));       Break;     end;end; procedure TExplorerForm.SBUpPathClick(Sender: TObject);var  contador : integer;beginfor contador := length(FActiveDir) downto 1 do   begin   if FActiveDir[contador] = '\' then      begin        FActiveDir := copy(FActiveDir,1,contador-1);        LoadDirectory(FActiveDir);        Break;      end;   end;end; finalization   explorer.Free;  explorer := Nil; end. ---------------------------------------------------------------------- lucamar · 2021-01-26 14:48 FileExist() should return True for an existing full-qualified file name ... except when the name is that of a directory; in that case it will return False and you'll have to use DirectoryExist(). ---------------------------------------------------------------------- torbente · 2021-01-26 16:14 Fixed! @Howadpc: how is the best way to handle the explorer form wjen the user finish with it? close? Destroy? visible :=false? Quote from: lucamar on January 26, 2021, 03:48:32 pmFileExist() should return True for an existing full-qualified file name ... except when the name is that of a directory; in that case it will return False and you'll have to use DirectoryExist(). ---------------------------------------------------------------------- howardpc · 2021-01-26 16:27 All classes must be freed after use to avoid memory leaks. There are various ways to do this. The code I gave you relied on a unit finalization routine. Alternatively you could make the Application the form's Owner (rather than having a Nil Owner), or you could explicitly Create and Free a form instance in your main form's code somewhere if you only need the form for a limited period in the lifetime of your app. It's up to you. ---------------------------------------------------------------------- Martin_fr · 2021-01-26 17:33 Quote from: torbente on January 26, 2021, 02:26:08 pmThanks a lot. I did not have idea i need declare a new class for the form. Note, you do not need to inherit TForm. The only thing that you need is a class. And you need to instantiate it. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TMyClass = class    procedure FSBOkFileOnClick(Sender: TObject);  end; ...   FSBOkFile := TSpeedButton.Create(Self);  with FSBOkFile do    begin...      OnClick := @FSBOkFileOnClick;  Thus the event is a method. Of course it makes sense to make your global vars, fields in that class. Just to keep some order (though order is a subjective concept). If you don't inherit TForm, then you could have a field "FMyForm: TForm".