I'm relatively new to delphi and I would like to know how to delete an entry in a dbGrid without using a dbNavigator but a button. The number of the entry that should be deleted must be entered in a spinedit, not be clicked on in the dbGrid. Thanks for any help.
First it's nice to position in the first record of the DataSet, then it will delete from the first to the N record.
DBGrid1.DataSource.DataSet.First;
Now you create the Loop (Don't forget to create the variable {var I : integer})
For I:=0 to SpinEdit1.Value-1 Do
Before start deleting records, you will need to verify if there is any record on DataSet.
You can do something like this:
if DBGrid1.DataSource.DataSet.RecordCount > 0 then
And finally you can delete the record
DBGrid1.DataSource.DataSet.Delete;
The final code would be like this:
DBGrid1.DataSource.DataSet.First; //Set on the first Record of the DataSet
For I:=0 to SpinEdit1.Value-1 Do //Do loop
if DBGrid1.DataSource.DataSet.RecordCount > 0 then //Check if have records
DBGrid1.DataSource.DataSet.Delete; //Delete
Related
I have a dbGrid in delphi RAD 10.3. I have a filter applied to only filter 3 of the records persay.
How do I iterate over those 3 records instead of the entire database? Is such a thing even possible?
Regards,
Romans.
The answer is simple: you can just loop through or iterate like normal. The ADOTable automatically filters the records so that when iterating it only displays those that match the filter.
THIS WILL ONLY HAPPEN IF:
Datamodule.ADOTable.filtered := true;
Rest of my code:
with Datamodule.ADOTable1 do
begin
DisableControls; //This is to stop the dbGrid from going crazy and showing the iteration.
First; //Sets first record in the filtered dataset to active record.
while not Eof do
begin
//do your print to screen or whatever you need the data for here. Example
showmessage(IntToStr(FieldByName('ID').value));
//this takes the active record's 'ID'-field and prints the value as a showmessage.
Next; //moves to next record.
end;
EnableControls;
end;
I have a table called Artist which currently contains four records and a TSQLQuery that contains the following statement:
SELECT name
FROM Artist
The table Artist also contains the following records:
id name
1 Artist 1
2 Artist 2
3 Artist 3
4 Artist 4
Current method of record retrieval:
Query1.Open;
for i := 0 to qArtist.FieldCount -1 do
with cbArtist.ListBox.ListItems[i] do
Text := qArtist.Fields[i].AsString;
Previous method of record retrieval:
Data bind Query1 to ComboBox1.
With the "previous" method, ComboBox1 would display all the expected records from the Artist table. However, when I try to use "current" method Query1 is only selecting the very first record from the Artist table despite there being three other existing values. I have tried the "current" method across other queries and they also returned only the first value of the table.
The reason I am going for this new approach is because I feel that I am very limited in what I can do if I continue to the "previous" / data bind method, but that is besides the point.
So how can I fix this problem? i.e. the problem of the query only selecting the very first record from the table.
You must use the Eof and Next methods to iterate over the records.
Query1.Open;
while not Query1.eof do
begin
cbArtist.Items.Add(Query1.FieldByName('Artist').AsString);
Query1.Next;
end;
You code show an interaction over fields, if you need iterate all record then you must use a code like:
Query1.Open;
Query1.first;
while not Query1.eof do
begin
with cbArtist.ListBox.ListItems[i] do
Text := qArtist.Fields[1].AsString; //put here field you want to bind on ListBox.
Query1.next;
end;
I don't think you are navigating your query's dataset correctly. The FieldCount and Fields[i] access the field metadata (columns going across), not the rows. I beleive in Delphi you use While not Eof begin... end.
Navigating Datasets
I would consider altering the data binding fields to suit your needs. Delphi's Databinding is very powerful. Manually iterating the dataset just to populate a control will just be extra code where bugs can hide. Utilize the built-in capabilities of the tools and it will be easier to understand and maintain.
I have a column in a DB table which stores pressure. The pressure is always stored as PSI and can be converted to BAR by diving by 14.5.
The user can toggle display of PSI/BAR with a Radio Group.
I was using a TStringGrid and am converting to a TDbGrid - which is quite new to me.
When the user toggles PSI/BAR, how to I update the display in my DB grid? (I imagine that I just execute it's query again? or Call query.Refresh()?) But how do I do the conversion?
Possibly a stored procedure, although that seems like overkill and stored procedurs are also new to me...
By changing the SELECT statement of my query? But how would I do that? SELECT pressure / 14.5 FROM measurements? Or how?
Or is there an OnBeforeXXX() which I can code? Or OnGetDisplayText() or some such?
I am sure thta this is very basic, but until now I have just been displaying unmanipulated data and now I need a conversion function. Google didn'ty help, but I probably didn't know what to ask for.
I also want to change the text of the column title, toggling between "Presure (PSI)" and "pressure (BAR)".
Thanks in advance for any help.
Code a OnGetText event handler for the pressure field like this:
type
TPressureMU = (pmuPSI, pmuBAR);
const
PSIToBarFactor = 1/14.5;
procedure TdmData.qMeasurementsPressureGetText(Sender: TField; var Text: string;
DisplayText: Boolean);
begin
case PressureMU of
pmuPSI: Text := FloatToStr(Sender.AsFloat); //Already PSI
pmuBAR: Text := FloatToStr(Sender.AsFloat * PSIToBarFactor); //ConvertingToBAR
end
end;
I'm using a property PressureMU of the declared enumeration to control if the pressure is shown in PSI or BAR measurement unit.
This way, when the user changes the selection, you just adjust the value of that property.
If you use persistent fields, you can link the event handler directly to you field using the object inspector, and if not, you can do it by code like this:
begin
qMeasurements.FieldByName('Pressure').OnGetText := qMeasurementsPressureGetText;
end;
where qMeasurementsPressureGetText is the name of the method.
Create persistent fields (right-click the query, and choose Add Fields to create fields at design time that are stored in the .dfm). Right-click the query, and add a new field. Make it a calculated field, and in the OnCalcFields event of the query do the conversion from PSI to BAR.
Now when the user toggles the display, you just display either the PSI or BAR column by setting the Column.FieldName, setting it to the actual PSI column or tne newly calculated BAR column.
Of course, if you're not using persistent fields, you can do it all in the query. Simply add a new column in your SQL statement that contains the result of the conversion, and you can still just change the Column.FieldName at runtime to toggle which value is being displayed.
while not TBLOrder.Eof do
begin
TBLOrder.Locate('OrderID', Total, []);
TBLOrder.Delete;
end;
This just deletes every single row in my Access Database, which is really annoying.
I'm trying to get the program to delete the selected row (which is Total).
From what I understand, It should locate the selected row, which is equal to Total. e.g. If Total = 3 it should find the row where OrderID = 3 and then delete that row.
Any help is appreciated.
Try this instead (Max's routine requires you to loop through the entire dataset, which is fine unless it's got many rows in it):
while (TblOrder.Locate('OrderID', Total, [])) do
TblOrder.Delete;
TDataSet.Locate returns a Boolean; if it's True, the found record is made the active record and you can then delete it. If it returns False (meaning the record is not found), the call to Delete is never made.
BTW, the problem with your original code is that you test for Eof, but never check to see if the Locate finds the record; you just delete whatever record you're on, and then test for Eof again. If you're not at Eof, you call Locate, ignore whether or not it found the record, and delete whatever row you're on. This then repeats over and over again until there are no more records, at which point Eof returns true and you break the loop.
If there is just one row that contains an ORDERID equal to 3, you don't need the WHILE loop.
If you expect more than one row with an ORDERID equal to 3, do this:
TBLOrder.first; // you could also do the locate here if it's a big table
while not TBLOrder.Eof do
begin
if TBLOrder.FieldByName('OrderID').AsInteger = 3 then
TBLOrder.delete
else
TBLOrder.next;
end;
Otherwise, you could also use SQL.
I'm trying to make tables inside tables in WORD. of course in finall program it will be dinamical, which is not in this sample.
Here is my sample code.
var
aTable, bTable, cTable : OLEVariant;
begin
m_WordApplication := CreateOleObject('Word.Application') ;
m_WordDocument := m_WordApplication.Documents.Add;
aTable := m_WordDocument.Tables.Add(m_WordApplication.Selection.Range, 2, 1);
aTable.Borders.Item(wdBorderLeft).LineStyle:=wdLineStyleSingle;
aTable.Borders.Item(wdBorderRight).LineStyle:=wdLineStyleSingle;
aTable.Borders.Item(wdBorderTop).LineStyle:=wdLineStyleSingle;
aTable.Borders.Item(wdBorderBottom).LineStyle:=wdLineStyleSingle;
bTable := m_WordDocument.Tables.Add(aTable.Cell(1, 1).Range, 2, 1);
bTable.Borders.Item(wdBorderLeft).LineStyle:=wdLineStyleSingle;
bTable.Borders.Item(wdBorderRight).LineStyle:=wdLineStyleSingle;
bTable.Borders.Item(wdBorderTop).LineStyle:=wdLineStyleSingle;
bTable.Borders.Item(wdBorderBottom).LineStyle:=wdLineStyleSingle;
cTable := m_WordDocument.Tables.Add(aTable.Cell(2, 1).Range, 3, 1);
cTable.Borders.Item(wdBorderLeft).LineStyle:=wdLineStyleSingle;
cTable.Borders.Item(wdBorderRight).LineStyle:=wdLineStyleSingle;
cTable.Borders.Item(wdBorderTop).LineStyle:=wdLineStyleSingle;
cTable.Borders.Item(wdBorderBottom).LineStyle:=wdLineStyleSingle;
m_WordDocument.SaveAs('C:/test.doc', False) ;
m_WordApplication.Quit(False);
Firstly i put new table(2 rows, 1 column) on position of the cursor, and then i try to put second table in cell(1,1) and third in cell(2,1) of the first table. second table has also 2 rows and 1 column, but third table has 3 rows and 1 column. but instead of what i want i get second and third table whit only one row, regardless if i putt something in thier cell or not.i always see only the last string i put in that table.
even more, if i put 1 row and 2 column table inside first table, than everything is normal.
can you help me.
thanks, Rok
When you have problems creating those tables in code, do the following:
Open Word
record a new macro
While recording, build the table you want, then stop the recording.
View your macro code in the Visual Basic Editor and try to translate that to OLE-automation code (which isn't that hard, it's almost the same)
aTable.Borders.Item(wdBorderVertical).LineStyle:=wdLineStyleSingle;
aTable.Borders.Item(wdBorderHorizontal).LineStyle:=wdLineStyleSingle;
You will have to do the same for bTable & cTable.
When you add more than 1 row/column, it will need border to separate it (i.e to separate 1 row from another OR separate 1 column from another).
Hope this helps.