I get Access violation error, but I know the code is correct, so where could be the problem? I'm trying to fill a ComboBox whit data from a local AccessDB.
var i : integer;
x : string;
begin
with DataModule3.ADOTable1 do begin
if RecordCount > 0 then
for i := 1 to RecordCount do begin
RecNo := i;
x := FieldByName('Teacher').AsString;
ComboBox1.Items.Add(x);
end;
end;
end;
I have tried lots of things and nothing works, I have tried lots of combobox typed but still doesn't work the only time a combobx showed value was when I selected a row in table then it showed in combobox the rows value by which I need to filter...
Access Violation is raised most probably because you have forgot to instantiate your datamodule DataModule3. Verify this by calling Assigned function.
begin
with DataModule3.ADOTable1 do
if Active then
while not Eof do
begin
ComboBox1.Items.Add(FieldByName('Teacher').AsString);
Next;
end;
end;
Related
I have a StringList with 2 rows
aOldPriceTerms[0] = 'FuelAddition=336643160'
aOldPriceTerms[1] = 'Freight=336643155'
So it works fine to use
aOldPriceTerms.Values['Freight'] -> '336643155'
I want to have a list of ID's from the list.
So simply
'FuelAddition','Freight'
Currently I use this code where aOldPriceTerms is the actual StringList.
function GetList(aOldPriceTerms: TStringList): String;
var
vOldTerm : string;
vOldTermsList : TStringList;
begin
vOldTermsList := TStringList.Create;
try
for i := aOldPriceTerms.Count - 1 downto 0 do
begin
vOldTerm := Copy(aOldPriceTerms[i], 1, (Pos('=', aOldPriceTerms[i])-1));
vOldTermsList.Add(vOldTerm);
end;
Result := vOldTermsList.CommaText;
finally
FreeAndNil(vOldTermsList);
end;
end;
My question is there a cleaner way to get the ids ?
Example is from the Delphi Basics, but TStringList.Names is also described in the Delphi documentation
// Now display all name and age pair values
for i := 0 to names.Count-1 do
begin
ShowMessage(names.Names[i]+' is '+names.ValueFromIndex[i]);
end;
You can use the TALNVStringList (NV for nameValue) from alcinoe (https://github.com/Zeus64/alcinoe) to handle Name and Value without all the time splitting the string
TALNVStringList is exactly the same as TStringList (nothing to change in the code except replacing TstringList by TALNVStringList) except that it's more efficient in speed because it's store the name in one dedicated field and the value in another dedicated field (no need to do all the time pos('=') and copy() to retrieve the name and the value of the row)
for i := 0 to aOldPriceTerms.Count-1 do
begin
ShowMessage(aOldPriceTerms.Names[i]+' is '+aOldPriceTerms.ValueFromIndex[i]);
end;
Exe demo showing the speed penalty of classic TstringList: https://svn.code.sf.net/p/alcinoe/code/demos/ALSortedListBenchmark/win32/ALSortedListBenchmark.exe
To get all the id's
for name in names.Names do
begin
i := names.IndexOf[name];
end;
or
To get all the Values
for name in vOldTermsList.Names do
begin
Value := vOldTermsList.Value[name];
end;
I would like to retrieve a value from a TDBGrid the selected row, how can I do?
procedure TForm7.Button2Click(Sender: TObject);
var
i, j: Integer;
s: string;
begin
if DBGrid1.SelectedRows.Count>0 then
with DBGrid1.DataSource.DataSet do
for i:=0 to DBGrid1.SelectedRows.Count-1 do
begin
GotoBookmark(DBGrid1.SelectedRows.Items[i]);
for j := 0 to FieldCount-1 do
begin
if (j>0) then s:=s+', ';
s := s + FindField(Fields.Fields[j].FieldName).AsString;
end;
Listbox1.Items.Add(s);
s:= '';
end;
end;
The code below fixes a few problems with yours.
The main problems were the fact that you weren't correctly initialising s and your way of getting the fields for the selected row(s) was flawed.
The calls to DataSet.Disable/EnableControls and ListBox1.Items.BeginUpdate/EndUpdate are to speed the process up.
Also, avoid the with construct like the plague. As you can see, my use of a local DataSet variable instead involves minimal extra typing and avoids all sorts of accidental problems that can arise when you use with.
procedure TForm1.GetSelected;
var
i,
J : Integer;
s : String;
DataSet : TDataSet;
begin
if DBGrid1.SelectedRows.Count>0 then begin
DataSet := DBGrid1.DataSource.DataSet;
// with DBGrid1.DataSource.DataSet do
try
ListBox1.Items.BeginUpdate;
DataSet.DisableControls;
for i:=0 to DBGrid1.SelectedRows.Count-1 do
begin
DataSet.GotoBookmark(Pointer(DBGrid1.SelectedRows.Items[i]));
s := '';
for j := 0 to DataSet.FieldCount - 1 do
begin
if (j>0) then s:=s+', ';
s := s + DataSet.Fields[j].AsString;
//s := s + FindField(Fields.Fields[j].FieldName).AsString;
end;
Listbox1.Items.Add(s);
//s:= '';
end;
finally
DataSet.EnableControls;
ListBox1.Items.EndUpdate;
end;
end;
end;
**Update: **
You can set the current grid row as selected like this
DBGrid1.SelectedRows.CurrentRowSelected := True;
Update #2
The grid's selected rows are stored in a TBookmarkList named SelectedRow. To clear the current selections, all you need do is to call its Clear method, like so:
procedure TForm1.btnClearSelectedClick(Sender: TObject);
begin
DBGrid1.SelectedRows.Clear;
end;
Equally, if you want to clear your ListBox, you just call its Clear method, as in:
procedure TForm1.btnClearListBoxClick(Sender: TObject);
begin
ListBox1.Clear;
end;
If you are having trouble getting my code to work, try this:
In the Object Inspector, set the DBGrid Options property dgMultiSelect to True.
Place a button on your form and in its OnClick handler call GetSelected.
Compile and run. Click a row in the grid and then the button. Nothing happens. The reason is that clicking the button moves focus away from the DBGrid, so that as far as it is concerned, none of its rows is selected. Then try step 3.
Run the app again. This time press and hold the Ctrl key while you click the grid then the button. This time the selected row appears in the ListBox. With the Ctrl button still down, click another row in the grid, then the button. This time, both rows are added to the ListBox.
only a single line call is needed to retrieve a value when you know the column number. The system will automatically return values from the current row, or last line clicked or double-clicked in a grid list.
var colidx : integer; ss : string;
..
ss := DBGrid1.Fields[colidx].AsString;
this is my method to retrieve the key
procedure TfrmArticle.Button2Click(Sender: TObject);
begin
op:='M';
with frmajtArticlee do
begin
Edit1.text:=dm.ADOQueryArticle.fieldbyname('ID_prod').Value;
Edit2.text:=dm.ADOQueryArticle.fieldbyname('ref_art').Value;
Edit3.text:=dm.ADOQueryArticle.fieldbyname('code_barre').Value;
Edit4.text:=dm.ADOQueryArticle.fieldbyname('marque').asstring;
Edit5.text:=dm.ADOQueryArticle.fieldbyname('qts_art').Value;
Edit6.text:=dm.ADOQueryArticle.fieldbyname('designation').asstring;
Edit7.text:=dm.ADOQueryArticle.fieldbyname('prix_achats_ht').Value;
Edit8.text:=dm.ADOQueryArticle.fieldbyname('prix_vente_faciliter').Value;
Edit9.text:=dm.ADOQueryArticle.fieldbyname('prix_vente_cache').Value;
val_mod:=dm.ADOQueryArticle.fieldbyname('ID_prod').asstring;
val_mod:string "declare it in public "
IF YOU'RE NEED TO FIND HOW TO GET DATA OF A SELECTED ROW IN THE DBGRID, I HAD THE HINT. I USE DBGRID.SelectedField TO GET THE SELECTED FIELD AND CONVERT THE FIELD TO BE SELECTED TO BOOKMARK BY DB_GRID.SelectedField.DataSet.Bookmark
AND USE DATASET.GOTOBOOKMART TO GO TO THE SELECTED RECORD FIELD IN DBGRID DB_DATA.DataSet.GotoBookmark (DB_GRID.SelectedField.DataSet.Bookmark);
With the code below I am trying to reload a DirectionsResult back into a TGMDirections.
procedure Form2.Button2Click(Sender: TObject);
var
DR: TDirectionsResult;
i: Integer;
begin
DR:= TDirectionsResult.Create(Form1.FDirection, 0);
DR.XMLData.BeginUpdate;
for i:= 0 to Memo1.Lines.Count - 1 do
begin
DR.XMLData.Append(Memo1.Lines[i]);
end;
DR.XMLData.EndUpdate;
ShowMessage(Form1.FDirection.DirectionsResult[0].Routes[0].Leg[0].EndAddress);
end;
All seems well until the ShowMessage where I get a List out of bounds message.
I take it that the DR has not been created or the Memo has not loaded into the DirectionsResult.
Further adaption has confirmed the DirectionsResult[0] does not exist.
Help with the correction would be greatly appreciated.
You can't add a TDirectionsResult to DirectionsResult array programatically, you need to invoke Execute method from TGMDirections object.
However you can do something like this
procedure TForm1.Button1Click(Sender: TObject);
var
DR: TDirectionsResult;
begin
DR:= TDirectionsResult.Create(GMDirection1, 1);
DR.XMLData.Text := Memo1.Lines.Text;
ShowMessage(DR.Routes[0].Leg[0].EndAddress);
end;
That is, you can work without problems with your object and you can access to all properties and methods.
Note the assignation between XMLData and Memo.Lines, don't assign line to line because the control of XML is made on OnChange event of XMLData.
Regards.
So here is my situation. I have a Form (MainMenu) and a Frame (TestFrame). TestFrame is displayed on a TPanel located on MainMenu. Using this code:
frTestFrame := TfrTestFrame.Create(nil);
frTestFrame.Parent := plMain;
frTestFrame.Align := alClient;
frTestFrame.Visible := true;
TestFrame displays fine with no error. TestFrame has a few TEdit boxes on it. A TButton on MainMenu calls a procedure located in TestFrame to check if the TEdit boxes text property is null.
procedure TfmMainMenu.tbCheckClick(Sender: TObject);
begin
frTestFrame.Check;
end;
This function on TestFrame is supposed to go through all the "TEdit" components and use the function GetErrorData that returns a string if the TEdit's text property is null. That string is added to a TStringList and displayed if any TEdit boxes are null.
function TfrTestFrame.Check: Boolean;
var
ErrorList: TStringList;
ErrorString: string;
I: Integer;
begin
ErrorList := TStringList.Create;
for I := 0 to (frTestFrame.ComponentCount - 1) do
begin
if (frTestFrame.Components[I] is TEdit) then
begin
ErrorString := GetErrorData(frTestFrame.Components[I]);
if (ErrorString <> '') then
begin
ErrorList.Add(ErrorString);
end;
end;
end;
if (ErrorList.Count > 0) then
begin
ShowMessage('Please Add The Following Information: ' + #13#10 + ErrorList.Text);
result := false;
end;
result := true;
end;
function TfrTestFrame.GetErrorData(Sender: TObject): string;
var
Editbox: TEdit;
ErrorString: string;
begin
if (Sender is TEdit) then
begin
Editbox := TEdit(Sender);
if (Editbox.Text <> '') then
begin
Editbox.Color := clWindow;
result := '';
end
else
begin
Editbox.Color := clRed;
ErrorString := Editbox.Hint;
result := ErrorString;
end;
end;
end;
The problem is that when it hits the line "for I := 0 to (frTestFrame.ComponentCount - 1) do
" It blows up and I get the error "Access violation at 0x00458... Read of address 0x000..."
I do not know why this error is happening. I can only assume that maybe the Frame is not getting creating. Any help would be great. Thanks in advance.
According to your question, the line
for I := 0 to (frTestFrame.ComponentCount - 1) do
leads to an access violation at address 0x000..... Now, for a start, why won't you tell us the precise error message with the full details? Hiding the address makes it harder!
Anyway, it looks like the address is going to be a value very close to zero. In any case the only explanation for an access violation there is that frTestFrame is invalid. Most likely it is nil.
I note that the code in question is inside a TfrTestFrame method. So why do you use frTestFrame to refer to the object? You are already inside an instance of the object. Do you have multiple global variables named frTestFrame? Perhaps one in the main form unit and one in the frame unit?
You should stop using global variables for your GUI objects. I know that the IDE leads you that way. Resist the temptation to program that way. Abuse of global variables leads to pain and suffering.
Since the code is inside a TfrTestFrame method you can use Self. In all your TfrTestFrame methods remove all references to frTestFrame. Your loop should be like this:
for I := 0 to ComponentCount - 1 do
and the rest of the methods in that class need similar treatment. Note that you don't need to explicitly write Self and it is idiomatic not to.
Finally, I urge you to learn how to use the debugger. It's a wonderful tool and if you would use it, it would have told you what the problem was. Don't be helpless, let the tools help you.
In this case I have some code which is working without problem in an existing program, but throws an excecption when I use it in a new program.
It may not be the best code, but it is working in every day use ...
Function DoSQlCommandWithResultSet(const command : String;
AdoConnection : TADOConnection;
resultSet : TStringList): Boolean;
var i : Integer;
AdoQuery : TADOQuery;
begin
Result := True;
resultSet.Clear();
AdoQuery := TADOQuery.Create(nil);
try
AdoQuery.Connection := AdoConnection;
AdoQuery.SQL.Add(command);
AdoQuery.Open();
i := 0;
while not AdoQuery.eof do
begin
resultSet.Add(ADOQuery.Fields[i].Value);
AdoQuery.Next;
Inc(i);
end;
finally
AdoQuery.Free();
end;
end;
Yes, it probably needs a try/catch and the boolean result isn't used, but it works ...
.... in the previous program, but in a new one it thows an exception when called ...
procedure TForm1.FormCreate(Sender: TObject);
var my_stringlist : TStringList;
i : integer;
begin
AdoConnection := TADOConnection.Create(nil);
if ConnectToDefaultDatabase(AdoConnection) = False then
MessageDlg('Agh !', mtError, [mbOK], 0);
my_stringlist := TStringList.Create();
if DoSQlCommandWithResultSet('show databases', AdoConnection, my_stringlist) = False then
MessageDlg('Urk !', mtError, [mbOK], 0);
for i := 0 to Pred(my_stringlist.Count) do
memo1.Lines.Add(my_stringlist.Strings[i]);
end; // FormCreate()
Now, here's the interesting part ... it throws the exception on Inc(i) and, if I replace that while loop with a for loop ...
for i := 0 to Pred(ADOQuery.Fields.count) do
resultSet.Add(ADOQuery.Fields[i].Value);
it works just fine.
I suppose that I could just use the for loop & move on, but I would like to understand what is going wrong .... can someone explain to me? Thanks
The first thing that jumps out at me is that
i := 0;
while not AdoQuery.eof do
begin
resultSet.Add(ADOQuery.Fields[i].Value);
AdoQuery.Next;
Inc(i);
end;
and
for i := 0 to Pred(ADOQuery.Fields.count) do
resultSet.Add(ADOQuery.Fields[i].Value);
are not semantically equivalent! When you call Next, you're advancing the current record in the dataset. A loop until you hit EOF will run through each record in the dataset once. But the second loop never calls Next and doesn't check for EOF; it's grabbing all the fields from one record.
If I had to guess what's causing the exception in the first loop, I'd say that you've got more records (rows) than fields (columns) in your dataset, and so after enough iterations, i ends up at ADOQuery.Fields.Count and you get an index out of bounds error.
What exactly are you trying to do here?
In the while loop you are looping as often as there are are records
while not AdoQuery.eof do
but then you access the Field
ADOQuery.Fields[i]
while i represents the current "record number"...
This would crash in any program only depending on the relationship between record count of the query versus field count of the query... as soon as record count is higher than field count it crashes...
The for loop is part of the fix for this...
BUT since you don't provide the information whether you need all fields for one record or all fields of all records as resultset it is hard to provide fixed code...