Понадобилось ручное листание страниц (определенного заранее числа записей на странице). Пользуюсь FireDAC, знаю, что там это уже реализовано, но после нескольких попыток понял, что хочу сделать такую штуку сам, поскольку явно пригодится в других языках и технологиях. Итак, вот что у меня получилось.
Проект выглядит примерно так…
Исходники
385_FireDacRowsFetching
Описание кода
Как видно здесь 2 FDQuery ,один из них основной, другой просто считает число записей в таблице. SQL запрос основного FDQuery должен быть без точки с запятой на конце, так как мы должны добавить инструкцию LIMIT StartRow, CountOfRows к основному запросу. Я здесь не делал акцент на подключении, а просто забил параметры подключения в FDConnection и подключил Queries к FDConnection визуально, хотя по хорошему, надо подключать их в коде. Но хотелось сосредоточиться на другом, именно на паджинаторе. Итак, основная работа у нас происходит в uDBPagination.
Требования к запросу SQL
В принципе он может быть любым SELECTом, главное, чтобы в его конец можно было добавить инструкцию LIMIT StartRow, CountOfRows
В моем случае это был
1 |
SELECT * FROM coffeetest_db.foldersandfilesonserver |
Функционал
-загрузить первую страницу
-загрузить следующую страницу
-загрузить предыдущую страницу
Далее описание юнитов
uDBPagination
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
unit uDBPagination; {< DB pagination for mySQL - adding LIMIT M,N phrase to Select Request} interface uses System.SysUtils, System.Classes, FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf, FireDAC.DApt.Intf, FireDAC.Stan.Async, FireDAC.DApt, Data.DB, FireDAC.Comp.DataSet, FireDAC.Comp.Client,Math; type TDBPagination = class(TDataModule) private FFDQuery:TFDQuery; FCountOfRecords:Int64; FCurrentRow:Int64; FCurrentPage:Integer; FCountOfPages:Integer; FRecordsOnPage:Int64; FInitialSQLText:string; public constructor Create(AOwner: TComponent); override; procedure LoadSomeLimitedNumberOfRows(AStartRow:Integer; ACountOfRows:integer); procedure CountNumberOfPages(); procedure LoadFirstPage(); procedure LoadNextPage(); procedure LoadPreviousPage(); property FDQuery:TFDQuery read FFDQuery write FFDQuery; property CurrentPage:Integer read FCurrentPage; property CountOfPages:Integer read FCountOfPages; property RecordsOnPage:Int64 read FRecordsOnPage write FRecordsOnPage; property InitialSQLText:string read FInitialSQLText write FInitialSQLText; property CountOfRecords:Int64 read FCountOfRecords write FCountOfRecords; end; implementation {%CLASSGROUP 'Vcl.Controls.TControl'} {$R *.dfm} { TDBPagination } procedure TDBPagination.CountNumberOfPages; begin Assert(FInitialSQLText<>'','FInitialSQLText is empty. Fill it First'); Assert(FRecordsOnPage<>0,'FRecordsOnPage is 0. Cannot be so Fill it First'); if Assigned(FDQuery) then begin with FDQuery do begin Disconnect(); SQL.Text:=FInitialSQLText; Open(); FCountOfPages:=Ceil(FCountOfRecords / FRecordsOnPage); Disconnect(); end; end; end; constructor TDBPagination.Create(AOwner: TComponent); begin inherited; FCountOfPages:=0; FCurrentPage:=0; FInitialSQLText:=''; end; procedure TDBPagination.LoadFirstPage; begin LoadSomeLimitedNumberOfRows(0,FRecordsOnPage); end; procedure TDBPagination.LoadSomeLimitedNumberOfRows(AStartRow:Integer; ACountOfRows:integer); begin if Assigned(FDQuery) then begin FDQuery.Disconnect(); // Attention here ! Original statement FDQuery.SQL.Text // should be without semicolon FDQuery.SQL.Text:=FInitialSQLText; FDQuery.SQL.Text:=FDQuery.SQL.Text + ' LIMIT :StartRow,:CountOfRows'; with FDQuery do begin Params.ParamValues['StartRow']:=AStartRow; Params.ParamValues['CountOfRows']:=ACountOfRows; end; FDQuery.Open(); end; end; procedure TDBPagination.LoadNextPage(); begin if FCurrentPage<FCountOfPages then begin FCurrentPage:=FCurrentPage+1; FCurrentRow:=FCurrentRow+FRecordsOnPage; LoadSomeLimitedNumberOfRows(FCurrentRow,FRecordsOnPage); end; end; procedure TDBPagination.LoadPreviousPage(); begin if FCurrentPage > 0 then begin FCurrentPage:=FCurrentPage-1; FCurrentRow:=FCurrentRow-FRecordsOnPage; LoadSomeLimitedNumberOfRows(FCurrentRow,FRecordsOnPage); end; end; end. |
uMain
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
unit uMain; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Error, FireDAC.UI.Intf, FireDAC.Phys.Intf, FireDAC.Stan.Def, FireDAC.Stan.Pool, FireDAC.Stan.Async, FireDAC.Phys, FireDAC.VCLUI.Wait, Data.DB, FireDAC.Comp.Client, FireDAC.Phys.MySQL, FireDAC.Phys.MySQLDef, FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf, FireDAC.DApt, Vcl.Grids, Vcl.DBGrids, FireDAC.Comp.DataSet, Vcl.StdCtrls, Vcl.ExtCtrls, // my units uDBPagination, Vcl.Samples.Spin; type TfMain = class(TForm) FDConnection: TFDConnection; pTopPanel: TPanel; bPrevious: TButton; bNext: TButton; qMainSelectRequest_WithoutSemiColonsOnTheEnd: TFDQuery; DataSource: TDataSource; DBGrid1: TDBGrid; bFirstPage: TButton; qCountOfRecords: TFDQuery; lPages: TLabel; SpinEdit: TSpinEdit; procedure bNextClick(Sender: TObject); procedure bFirstPageClick(Sender: TObject); procedure bPreviousClick(Sender: TObject); procedure SpinEditChange(Sender: TObject); private FDBPagination:TDBPagination; procedure SetupDBPaginationModule; public constructor Create(AOwner: TComponent); override; end; var fMain: TfMain; implementation {$R *.dfm} procedure TfMain.bNextClick(Sender: TObject); begin FDBPagination.LoadNextPage(); lPages.Caption := FDBPagination.CurrentPage.ToString() + ' / ' + FDBPagination.CountOfPages.ToString; end; procedure TfMain.bPreviousClick(Sender: TObject); begin FDBPagination.LoadPreviousPage; lPages.Caption := FDBPagination.CurrentPage.ToString() + ' / ' + FDBPagination.CountOfPages.ToString; end; procedure TfMain.bFirstPageClick(Sender: TObject); begin FDBPagination.LoadFirstPage; lPages.Caption := FDBPagination.CurrentPage.ToString() + ' / ' + FDBPagination.CountOfPages.ToString; end; constructor TfMain.Create(AOwner: TComponent); begin inherited; SetupDBPaginationModule; FDBPagination.LoadFirstPage; lPages.Caption := FDBPagination.CurrentPage.ToString() + ' / ' + FDBPagination.CountOfPages.ToString; end; procedure TfMain.SetupDBPaginationModule; begin if not Assigned(FDBPagination) then FDBPagination := TDBPagination.Create(Self); with qCountOfRecords do begin Open; FDBPagination.CountOfRecords := FieldByName('CountOfRecords').AsLargeInt; Close; end; FDBPagination.FDQuery := qMainSelectRequest_WithoutSemiColonsOnTheEnd; // Attention here ! Original statement FDQuery.SQL.Text // should be without semicolon FDBPagination.InitialSQLText : = qMainSelectRequest_WithoutSemiColonsOnTheEnd.SQL.Text; FDBPagination.RecordsOnPage :=SpinEdit.Value; FDBPagination.CountNumberOfPages; end; procedure TfMain.SpinEditChange(Sender: TObject); begin if Assigned(FDBPagination) then begin if SpinEdit.Value=0 then begin SpinEdit.Value:=1; ShowMessage('Value cannot be 0. Number of Records on Page > 0'); Exit; end; FDBPagination.RecordsOnPage := SpinEdit.Value; FDBPagination.CountNumberOfPages; bFirstPageClick(Self); end; end; end. |