How to extract Object of a BindSource in Delphis LiveBindings? - delphi

In Delphi 10.1 I have an ObjectList named DogCollection
and each entry is of the type TDog, a custom class.
thanks to tutorials from malcolm groves I was able to populate a Stringgrid
with my DogCollection.
http://www.malcolmgroves.com/blog/?p=1084
Now I'd like to be able to scroll through the stringgrid and everytime I scroll I want to update the variable "CurrentDog" from the type TDog, with whatever Object is highlighted in the stringgrid.
So I have an Overview about my DogObjects and also a single Object of my Dog
which I can independently view/manipulate.
I am out of ideas at this point.
If it is of any help to you, I can also not get the AfterScroll events of the Adapter to trigger, not even when I add a Navigator with RightClick->Add Navigator.
I thank you for your help and time.

Not sure to understand the question but I think you don't need to have a variable "CurrentDog" to work on the selected object of your list.
You can create all the components (TEdit) you need for your dog (Name, Age...) and bind these components to the same fields (Name, Age...) in your TDataGeneratorAdapter (which is linked to the "Adapter" property of your TAdapterBindSource).
Then, when you select a row in your grid, the corresponding object appears in your edit components. When you modify the "Text" properties, the grid is updated.
EDIT : InternalAdapter
After few searches, you can get your object with the InternalAdapter of your TAdapterBindSource
On the OnClick event :
procedure TForm1.Button1Click(Sender: TObject);
var
Adapter: TBindSourceAdapter;
begin
Adapter:= AdapterBindSource1.InternalAdapter;
CurrentDog:= TDog(Adapter.Current);
end;

Related

Key value for this row was changed or deleted at the data store. The local row is now deleted

Migrating 1.5 million lines of D7,BDE,Paradox to XE2,ADO,MS-SQL.
We have a TDBLookupComboBox that works fine. We provide the user with an ellipsis button so they can add or delete records from the combo box's ListSource table while the combo box is visible.
If the user clicks on the ellipsis, we let them edit the table and then we Refresh the comboboxes datasource, like this:
EditTable.ShowModal; // user edits ListSource.Dataset table
Form1.DBComboBox1.ListSource.DataSet.Refresh
This worked fine in the Paradox world.
In the SQL/ADO world, if the user deletes a record from the ListSource, we get the message on the Refresh statement above:
Key value for this row was changed or deleted at the data store.
The local row is now deleted.
This occurs even if the record the user deleted was not the currently selected item in the combo box.
We don't understand why this is happening now but not in the Paradox version.
Our solution has been (after the user edits) to close and open the ListSource dataset as shown below, but this is clumsy (and we'll have to replicate in almost 100 places we do this kind of thing.)
Here's our current fix:
var
KeyBeforeUserEdit: Integer;
KeyBeforeUserEdit:= Form1.DBComboBox.KeyValue;
EditTable.ShowModal; // user edits ListSource.Dataset table
Form1.DBComboBox1.ListSource.DataSet.Close;
Form1.DBComboBox1.ListSource.DataSet.Open;
if Form1.DBComboBox1.ListSource.DataSet.Locate('UniqueKey', KeyBeforeUserEdit, []) then
From1.DBComboBox1.KeyValue := KeyBeforeUserEdit;
Any alternate suggestions or explanations why this is necessary?
I can't know for sure what is going on but you may be able to simplify your migration (albeit not good practice) in the following way.
ShowModal is a virtual function so you can override it in the class EditTable belongs to (TEditTable?) providing that you have that source. Within the unit add the Form1 unit to the uses clause in the implementation section (if it is not already there) and add your override as follows
function TEditTable.ShowModal : integer;
var
KeyBeforeUserEdit: Integer;
begin
KeyBeforeUserEdit:= Form1.DBComboBox.KeyValue;
Result := inherited ShowModal; // user edits ListSource.Dataset table
Form1.DBComboBox1.ListSource.DataSet.Close;
Form1.DBComboBox1.ListSource.DataSet.Open;
if Form1.DBComboBox1.ListSource.DataSet.Locate('UniqueKey', KeyBeforeUserEdit, []) then
From1.DBComboBox1.KeyValue := KeyBeforeUserEdit;
end;
It is a bit of a kludge but may be pragmatic and save a lot of work.

associate a row of a list box with a row of a DB table

How can i associate a row of a list box with a row of a DB table?
I created a firemonky mobile application. On the form i drop a listbox. The listbox is filled with an item from a database.
zQuery1.Close;
ZQuery1.Open;
ListBox1.Items.Clear;
While not ZQuery1.Eof do
Begin
ListBox1.Items.Add(ZQuery1.Fields[1].AsString);
ZQuery1.Next;
end;
ZQuery1.Close;
Displayed is the name of a person.
Now when i doubleclick on the name of the person i want the open a second form with detailed information from that person (available in record). Can someone help me if this can be done?
I want to create an app on iPad so when someone taps on the detail button in the listbox there is more information of that person.
Instead of using listbox.items.add, you need to use listbox.items.addobject. For the purposes of the code below, I am assuming that the primary key of the table will be returned in zquery1.fields[0].
zQuery1.Close;
ZQuery1.Open;
ListBox1.Items.Clear;
While not ZQuery1.Eof do
Begin
ListBox1.Items.AddObject (ZQuery1.Fields[1].AsString,
tobject (zquery1.fields[0].asinteger)
ZQuery1.Next;
end;
ZQuery1.Close;
In order to access the stored id, you would write something like this
avariable:= longint (listbox1.Items.Objects[listbox1.itemindex]);

Delphi - Passing data from a TQuery to Quickreport component (QRDBText)

This may be a newbie question, but I was unable to solve this problem.
I have a TQuery component (Query1) wich returns me a dataset from a database.
I would like to pass the records in it to a QuickReport QRDBText component - with no success so far.
I am creating the Query1 in run-time, and then an event (button press) would come up with the Quickreport. I got this far with it:
QReport.DataSet:=Query1;
QRDBText1.DataSet:=Query1;
QRDBText1.Datafield:='Vnev'; //first field in query
QRDBText2.DataSet:=Query1;
QRDBText2.Datafield:='Knev'; //second field in query
QRDBText3.DataSet:=Query1;
QRDBText3.Datafield:='Idcard'; //third field in query
But this shows me the big grey nothing.
If I operate with a sample database and place a TTable on the QuickReport, and set up the connections via the Object Inspector, it works. But again: I need to display data from my Query.
Any help would come handy! Thanks
The QRdetailband should set to true and fill the QRdbtext on it.

Selection Changed Event in TreeviewUsing Delphi

I have one doubt i doing one delphi application.If i select the treeview node at run time display the form in every selected nodes.how its work?.please help me
In Tree View Component, Every node was represented by TTreeNode object, this object has index of selected node, text, etc..
If you want to show form for every node double click
just add the following code
if Treeview1.Selected.Text = 'first_form' then
begin
TFirst_Form.create(Application)
TFirst_Form.show
end;

How to change behaviour of TDBNavigator component?

I would like to change the behaviour of the insert button on the standard DBNavigator bar, from a dataset insert to append.
I could trap the button click in the BeforeAction event, do the append, etc; and then in the OnClick event abort the original insert, but this seems a bit of a hack. Any better ideas? I'm using D6 (500,000 kms on the clock, and still going strong...).
Thanks for any advice
Regards,
PhilW.
You could derive your own class from TDBNavigator and override BtnClick method.
Or, for a quick and dirty fix, you could change the insert button's click handler at runtime, e.g.:
type
THackDBNavigator = class(TDBNavigator);
procedure TForm1.DBNavigatorInsertClick(Sender: TObject);
var
DBNavigator: TDBNavigator;
begin
DBNavigator := ((Sender as TControl).Parent as TDBNavigator);
if Assigned(DBNavigator.DataSource) and (DBNavigator.DataSource.State <> dsInactive) then
begin
if Assigned(DBNavigator.BeforeAction) then
DBNavigator.BeforeAction(DBNavigator, nbInsert);
DBNavigator.DataSource.DataSet.Append;
if Assigned(DBNavigator.OnClick) then
DBNavigator.OnClick(DBNavigator, nbInsert);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
THackDBNavigator(DBNavigator1).Buttons[nbInsert].OnClick := DBNavigatorInsertClick;
end;
There is no difference in most databases between insert and append. Doing an actual physical insert would mean actually moving all data, starting with the place the new row would be inserted, down the size of one row, and then writing that new row in the newly open spot. This would be very slow because of all of the disk activity.
Databases instead do an append, which writes the data to the end of the physical file, and the index order controls the way the row appears to be positioned in the correct place in the file.
So for most intents and purposes, you're probably already getting an append instead of an insert, regardless of which method you use or what the button on the DBNavigator says. It's the index that makes it appear otherwise.
You can check that for validity by creating a database without an index, and try doing both an insert and an append a few times, examining the data carefully after every operation.
#TOndrej: Great! I hadn't appreciated this technique. Thanks!
#Ken White: I understand your point, but visually to my users it makes a difference - the DBNavigator controls a DBGrid where, in the majority of cases, there is plenty of unused rows in the grid. It appears to be more consistent to have new records appear at the bottom of the grid rather then just above where ever the current record is at that moment. But thanks for your answer.
Regards,
PhilW.

Resources