DelphiXE2 : How to Display Data From Database to NextGrid - sql

I've searched around, ask my local teacher at my school. But there is no way i could find how to display data from database to NextGrid. I used SELECT * but it doesnt shown on the next grid.
ZQuery1.Close;
ZQuery1.SQL.Clear;
ZQuery1.SQL.Text := 'SELECT * FROM tb_siswa';
ZQuery1.Open;
NextGrid1.ClearRows;
x:= 0;
while not ZQuery1.Eof do
begin
NextGrid1.AddRow();
NextGrid1.Cell[0,x].AsString := ZQuery1.Fields[0].AsString;
NextGrid1.Cell[1,x].AsString := ZQuery1.Fields[1].AsString;
NextGrid1.Cell[2,x].AsString := ZQuery1.Fields[2].AsString;
NextGrid1.Cell[3,x].AsString := ZQuery1.Fields[3].AsString;
NextGrid1.Cell[4,x].AsString := ZQuery1.Fields[4].AsString;
inc(x);
ZQuery1.Next;
end;
ZQuery1.Close;
Any support please?

On the nex suite component page, you have the NxDataCellSource component. Place it on the form. Place the next grid on the form. In the NxDataCellSource properties, change the datasource to the one you are using, ZQuery1. Change the Associate properties to NextGrid1 (or other name you have associated to nextgrid). Set active to true and enjoy.

i'm use NextGrid1.RowCount-1 this code,dont need manual increment value
while not ZQuery1.Eof do
begin
NextGrid1.AddRow();
NextGrid1.Cell[0,NextGrid1.RowCount-1].AsString := ZQuery1.Fields[0].AsString;
Next;
end;

Related

Adressing multiple, similar variables/objects in Delphi

I am writing a program that uses many shapes and I need to create a procedure to turn them all white. The shapes in question are named SectorBorder1 to SectorBorder20.
Is there a way to adress the shapes like this or similarly?
SectorBorder[X].brush.color := ClWhite;
Inc(X);
...where X is the number (obviously), instead of having to do:
SectorBorder1.brush.color := ClWhite;
SectorBorder2.brush.color := ClWhite;
...
SectorBorder20.brush.color := ClWhite;
So basically being able to differentiate names through a variable. This is the only way I could think of describing it. (Sorry, could someone also maybe include a better discription?) Any advice would be greatly apreciated.
Use an array
private
SectorBorders: array[1..20] of TShape;
procedure TMyForm.FormCreate(Sender: TObject):
begin
SectorBorders[1] := SectorBorder1;
..
SectorBorders[20] := SectorBorder20;
end;
procedure TMyForm.SetAllToWhite;
var
X: Integer;
begin
for X := Low(SectorBorders) to High(SectorBorders) do
SectorBorders[X].Brush.Color := clWhite;
end;
As an alternative to an array you can use a List. If you use the TList from System.Generics.Containers you can easily pass through all elements of the list.
You use it like this:
interface
uses System.Generics.Collections; // you need this to get the TList, you also need your other refereces of course
...
protected
pShapes: TList<TShape>;
procedure TMyForm.FormCreate(Sender: TObject):
var
nLoop: Integer;
begin
Self.pShapes:=TList<TShape>.Create;
nLoop:=0;
while(nLoop<Self.ComponentCount) do
begin
if(Self.Components[nLoop] is TShape) then
pList.Add(TShape(Self.Components[nLoop]));
Inc(nLoop);
end;
end;
procedure TMyForm.SetAllToWhite;
var
pShape: TShape;
begin
for pShape in Self.pShapes do
pShape.Brush.Color := clWhite;
end;
Choosing whether you want to use an array or a TList will be partially down to preference, but also down to what else you may want to do with the collection of TShapes and how they are managed within the object.
You can use the FindComponent method of the form.
shape:= FindComponent('SectorBorder' + IntToStr(i)) as TShape;
if shape <> nil then
pShape.Brush.Color := clWhite;
See http://docwiki.embarcadero.com/CodeExamples/Sydney/en/FindComponent_(Delphi)

Is possible to set filter option in SMDBGrid to CaseInsensitive?

I have SMDBGrid component with show filter bar option set to true, but filter just working in case-sensitive mode
1.Try with lower case
2.Try with upper case
I have tried to insert the code in SMDBgrid.pas like this
procedure TSMDBGrid.ApplyFilter;
var
TempCol: Integer;
begin
if (eoFilterAutoApply in ExOptions) then
begin
TempCol := LeftCol;
BeginUpdate;
try
if DataLink.Active then
begin
DataLink.DataSet.CheckBrowseMode;
DataLink.DataSet.Filtered := False;
DataLink.DataSet.OnFilterRecord := nil;
DataLink.DataSet.OnFilterRecord := OnFilterRecord;
DataLink.DataSet.FilterOptions := [foCaseInsensitive]; <-- this the inserted code
DataLink.DataSet.Filtered := not FilterIsEmpty();//True;
end;
finally
LeftCol := TempCol;
EndUpdate;
end;
end;
if Assigned(OnFilterChanged) then
OnFilterChanged(Self);
end;
But no luck, Is posible filter the record ignoring the case?
PS:
I use Delphi 2009
You may use the OnAccentStringConvert event to transform the value for filter in column before compare:
begin
Result := UpperCase(S)
end;
Looks like I cope with this problem too. Trying to find any solution for Delphi XE 10.3 community edition and wrote to author of SMDBGrid and he found workaround.
Please use SQL ADOQuery as follows.
SELECT UPPER(field) FROM your_table
then use event OnAccentStringConvert and uppercase S String as follows:
function TYourFormName.DBGrridNameAccentStringConvert(Sender: TObject; const S: string): string;
begin
Result := UpperCase(S)
end;
This works very ugly, but at least works. Or you may just create filter by yourself for every table.

delphi - Save multiple records at once

Dont know how to formulate this exactly so bear with me please... I am saving text from a memo to a database with date selected in the PlannerCalendar1. Since I can select multiple dates in the PlannerCalendar1, how can I post the value of the memo to all dates selected in the PlannerCalendar1?So when I click 'save' the contents of the memo gets saved to all selected dates.Database is SQLite. The table also has an ID field which is autoinc (primary).PlannerCalendar is from the set of TMS components.
procedure TForm1.cxButton1Click(Sender: TObject);
var i:integer;
begin
with UniQuery1 do
begin
UniQuery1.SQL.Text:='INSERT INTO LOG (DATE,PERSON,DONE,TIME) VALUES (:a1,:a2,:a3,:a4)';
UniQuery1.PARAMS.ParamByName('A1').VALUE := PlannerCalendar1.Date;
UniQuery1.PARAMS.ParamByName('A2').VALUE := cxmemo1.Lines.text ;
UniQuery1.PARAMS.ParamByName('A3').VALUE := (0);
UniQuery1.PARAMS.ParamByName('A4').Value := AdvOfficeStatusBar1.Panels[0].Text;
UniQuery1.ExecSQL;
cxmemo1.Clear;
UniTable1.Refresh;
Tried this at the end but it wont work :
with plannercalendar1.Dates do
begin
for i := 0 to -1 do
begin
UniQuery1.PARAMS.ParamByName('A1').VALUE :=plannercalendar1.dates.Add + i ;
UniQuery1.ExecSQL;
end;
I have no idea what a PlannerCalendar is, but presumably there's some way to get at the list of dates that are selected. You want to do something like this:
UniQuery1.SQL.Text:='INSERT INTO LOG (DATE,PERSON,DONE,TIME) VALUES (:a1,:a2,:a3,:a4)';
UniQuery1.PARAMS.ParamByName('A2').VALUE := cxmemo1.Lines.text ;
UniQuery1.PARAMS.ParamByName('A3').VALUE := (0);
UniQuery1.PARAMS.ParamByName('A4').Value := AdvOfficeStatusBar1.Panels[0].Text;
for i := 0 to PlannerCalendar1.NumberOfDatesSelected-1 do begin
UniQuery1.PARAMS.ParamByName('A1').VALUE := PlannerCalendar1.SelectedDate[i];
UniQuery1.ExecSQL;
end;
Of course, NumberOfDatesSelected and SelectedDate are wild guesses. You'll need to find out what they're really called.
You need to use the Planner's SelectionToAbsTime method :-
Var
lStart, lEnd : TDateTime;
Begin
Planner1.SelectionToAbsTime(lStart, lEnd);
For I := Trunc(lStart) To Trunc(lEnd) Do
SaveMemosForDate(I);
End;

SQL table change sequence

I have a table displayed in DBGrid the sort order is based on a Sequence field and I want to be able to move an item up or down one place at a time. I have researched here and cannot find anything exactly like I need.
The problem comes when I disable the Sort order to make a change, the list reverts to the order in which the data was originally entered, so I have lost the item next in line.
This is what I have...
Folder Sequence
----------------
Buttons 1
Thread 2 << Current Row
Cotton 3
Rags 4
On clicking the "MoveDown" button I want...
Folder Sequence
----------------
Buttons 1
Cotton 2
Thread 3 << Current Row
Rags 4
But - when I remove the Sort order on Sequence I get the order I entered the items...
Folder Sequence
----------------
Buttons 1
Cotton 2
Rags 4
Thread 3 << Current Row
So far my attempts are proving pretty cumbersome and involve loading the Rows into a listbox, shuffling them and then writing them back to the Table. Gotta be a better way, but it is beyond my current grasp of SQL.
Can someone please point me in the direction to go.
I don't want to trouble anyone too much if it is a difficult thing to do in SQL, as I can always stay with the listbox approach. If it is relatively simple to an SQL-expert, then I would love to see the SQL text.
Thanks
My solution is based on the TDataSet being sorted by the Sequence field:
MyDataSet.Sort := 'Sequence';
And then swapping the Sequence field between the current row and the Next (down) / Prior (up) records e.g.:
type
TDBMoveRecord = (dbMoveUp, dbMoveDown);
function MoveRecordUpDown(DataSet: TDataSet; const OrderField: string;
const MoveKind: TDBMoveRecord): Boolean;
var
I, J: Integer;
BmStr: TBookmarkStr;
begin
Result := False;
with DataSet do
try
DisableControls;
J := -1;
I := FieldByName(OrderField).AsInteger;
BmStr := DataSet.Bookmark;
try
case MoveKind of
dbMoveUp: Prior;
dbMoveDown: Next;
end;
if ((MoveKind = dbMoveUp) and BOF) or ((MoveKind = dbMoveDown) and EOF) then
begin
Beep;
SysUtils.Abort;
end
else
begin
J := DataSet.FieldByName(OrderField).AsInteger;
Edit;
FieldByName(OrderField).AsInteger := I;
Post;
end;
finally
Bookmark := BmStr;
if (J <> -1) then
begin
Edit;
FieldByName(OrderField).AsInteger := J;
Post;
Result := True;
end;
end;
finally
EnableControls;
end;
end;
Usage:
MoveRecordUpDown(MyDataSet, 'Sequence', dbMoveDown);
// or
MoveRecordUpDown(MyDataSet, 'Sequence', dbMoveUp);
If i understand right, you want to find "next sequence item"?
May be you may do something like "get first min value, grater then X"?
In this way, you can pass prev. Row sequence value.

Variable structure for database results

A lot of times when we query the database, we just need one column with varchar.
So I've made a nice function for querying the database and putting the results in a stringlist:
function Getdatatostringlist(sqlcomponent, sqlquery: string): TStringlist;
What I'm looking for now is basically the same function but for results with multiple columns where you don't know in advance what type the data is, be it varchar, int, datetime.
What kind of datastructure would be good to use here.
The reason I want this is that I try not to work on open datasets. I like much more to fetch all results into a temporary structure, close the dataset and work on the results.
After Kobiks reply about using in Memory datasets I came up with the following, it's fast put together to test the concept:
procedure TForm1.Button2Click(Sender: TObject);
var
MyDataSet : TAdoDataSet;
begin
MyDataSet := GetDataToDataSet('SELECT naam FROM user WHERE userid = 1', ADOConnection1);
try
Form1.Caption := MyDataSet.FieldByName('naam').AsString;
finally
MyDataSet.free;
end;
end;
function TForm1.GetDataToDataSet(sSql: string; AdoConnection: TADOConnection): TAdoDataSet;
begin
Result := TAdoDataSet.Create(nil);
Result.LockType := ltBatchOptimistic;
Result.Connection := AdoConnection;
Result.CommandText := sSql;
Result.Open;
Result.Connection := nil;
end;
I think this is something to build on.
You should use any disconnected in-memory TDataSet descendant, such as TClientDataSet.
Do not attempt to re-invent the wheel by storing a record-set in some new "Variant" structure. A TClientDataSet already contains all features you need to manipulate a "temporary" data structure.
Here is how you create a TClientDataSet structure:
cds.FieldDefs.Add('id', ftInteger);
cds.FieldDefs.Add('name', ftString, 100);
// ...
// create it
cds.CreateDataSet;
// add some data records
cds.AppendRecord([1, 'Foo']);
cds.AppendRecord([2, 'Bar']);
Many TDataSets has an ability to be used as an in-memory (client) datasets depending on the provider and LockType, for example a TADODataSet with LockType=ltBatchOptimistic could fetch results-set from the server, and then remain disconnected.
For exchanging Data with Excel this structure is usefull, might be useful for other purposes.
Function GetDatasetasDynArray(Ads: TDataset; WithHeader: Boolean = true): Variant;
// 20130118 by Thomas Wassermann
var
i, x, y: Integer;
Fields: Array of Integer;
begin
x := 0;
y := Ads.RecordCount;
if WithHeader then
inc(y);
SetLength(Fields, Ads.FieldCount);
for i := 0 to Ads.FieldCount - 1 do
if Ads.Fields[i].Visible then
begin
Fields[x] := i;
inc(x);
end;
SetLength(Fields, x);
Result := VarArrayCreate([0, y - 1 , 0, length(Fields) - 1], VarVariant);
y := 0;
if WithHeader then
begin
for i := Low(Fields) to High(Fields) do
begin
Result[y, i] := Ads.Fields[Fields[i]].DisplayLabel;
end;
inc(y);
end;
try
Ads.DisableControls;
Ads.First;
while not Ads.EOF do
begin
for i := Low(Fields) to High(Fields) do
begin
Result[y, i] := Ads.Fields[Fields[i]].Value;
end;
Ads.Next;
inc(y);
end;
finally
Ads.EnableControls;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
DynArray:Variant;
begin
DynArray := GetDatasetasDynArray(Adodataset1,true);
//DynArray[0,x] Header or First row
//DynArray[1,x] First row or SecondRow
Excel.Range.Value := DynArray;
end;
Why don't you like working with open datasets? They usually do not block the server. Copying the data from the dataset to whatever you want is extra overhead which is most likely not necessary.
A dataset provides exactly the functionality you want: A matrix with variable columns and rows.
EDIT: However, if you have iterate through the dataset often, you should consider creating a class holding the relevant information and then copy the data into a generic list, dictionary, tree or whatever you need as fast lookup structure.
Of course you could think of building something smart which can be as flexible as a dataset but: The more general things get, the poorer the performance (usually).