How do I add row programatically in TDBGrid in Delphi - delphi

I want to add some data in grid to show user.
I want to use TDBGrid
How do I add any row to grid without a database?
Thank you

A TDBGrid reflects the data in an underlying dataset (query, clientdataset etc). To have new or changed data appear, update the data in the dataset (and/or maybe refresh it).
If you do not use an external database, your are still able to use e.g. a TClientDataSet and store its data to file (proprietary format or XML, depending on your Delphi version - see its documentation). Given the flexibility of using datasets (e.g. editing the data), I recommend this.
Alternatively, use a TstringGrid and store your data in any (other) way you want.
[It also depends on what else you want to do with the data once it's presented in the grid. If you want the user to be able to edit it, TClientDataSet is the way to go.]

Related

Stop Auto Edit working in TDBGrid

D5, ZEOS 6.6, SQLite.
I have srcAccount.AutoEdit = False;
I have All Edit functions set to False in the TDBGrid Options. Only options are set to true are Indicator, grind lines and Titles.
I have a form with a few TDBEdits and a TDBGrid on it showing all the current accounts.
When the user clicks the "New" button for a new account I have
dbedAcct.SetFocus;
tblAccounts.Insert;
If, after clicking the New button, the user wants to scroll to check Account names OR happens to click in the grid, it saves the new data and drops out of Insert mode.
How can I stop this happening? I need for them to be able to check account names.
Or, is this a bug with D5? If so, how do I work around it?
I also tried using SMDBGrid and it does exactly the same thing.
http://www.scalabium.com/smdbgrid.htm
I need for them to be able to check account names.
You can't do that using the same grid + dataset if you're allowing the user to do data entry to the grid. You are creating this problem for yourself by trying to use the grid for data entry and look-up at the same time. A simple solution is to use the grid for look-up and have a separate form (or panel on the same form as the grid) for doing the inserts, and these need to be connected to different dataset instances.
The thing is, you can't scroll around a dataset (as you need to do you look up other records) while it's in the middle of inserting a record. The dsBrowse state needed for a dataset to allow scrolling around and the dsInsert state needed to insert are mutually exclusive. Attempting to scroll the dataset will automatically post the pending insert, as you've found.
So, you actually need two dataset instances, one for lookup and one for inserts. If you use two client-side ClientDataSet instances, it can be quite straightforward because of the ease with which you can copy the data from one to the other using the CDS's Data property (cdsLookup.Data := cdsLive.Data), so making a local copy for look-up is trivial. Or, if you prefer you can use a cloned cursor - see http://edn.embarcadero.com/article/29416

How do I determine when a record is inserted in a TDataSet?

I am writing a grid control that will display the contents of either a TDataSet or a TObjectList. When you only need to support TDataSet, things are quite simple:
Link to the dataset via a TDataLink descendant.
When painting the contents of the grid, you can use the records buffered in that TDataLink to paint what you need to.
There is no need to have individual objects somewhere to represent rows in the TDataSet, because you always just paint the rows in the buffer.
In my case, I need to accept data from a few other sources as well, which meant that I needed to have an object representing each row (also because the control required quite a bit of row state).
But this causes problems with the model described above. Because I have an object representing each row, I need to be informed when records are added or deleted from the TDataSet. And I just cannot see how to do that.
Clearly, I don't want to be hooking to the dataset events; they may already be in use and the TDataLink is meant to be the mediator between my control and the dataset. And my attempts at using the DataEvent virtual method failed, because it simply doesn't tell you if a record is being added/deleted.
Any ideas?
If you hook your TDataLink descendant to a TDataSource that is connected to the TDataSet you get a call in the RecordChanged procedure when data changes.
You can use the events OnDataChange and OnUpdateData of a TDataSource connected to the TDataSet.
It seems, you have to derive your own class from the base dataset class you are going to use. There you will need override InternalAddRecord, InternalPost, InternalDelete methods and handle records addition / deletion.

Can Dataset filtering be done from outside the dataset?

I have two TDBLookupComboBox controls that I'd like to connect to the same dataset, but have each one display a different subset of the data. If I only needed one box, I'd use filtering on the dataset, but I need to be able to display both of them at the same time, and I'm not aware of any way to do that. Does anyone know if it can be done, and if so, how?
If you're using a TClientDataSet, you can clone the cursor (TClientDataSet.CloneCursor) into another TClientDataSet that doesn't have the ProviderName property set. Both ClientDataSet now point to the same data in memory but can have their own filters.

Multiselect listbox bound to database in Delphi 6

I'm using Delphi 6, and I want a database bound list box with multiselect. I found three types of List boxes: TListBox, TDBListBox and TDBLookupListBox.
As far as I can understand, TListbox is not bound to database. TDBListBox and TDBLookupListBox can't be multiselected.
Is there a way to get a multiselect listbox binded to database?
The problem with databinding components is that they rely on a datasource and a datasource has only a single cursor. That is probably the reason why.
By the way, do you need to change the data? Else you could fill a normal listbox from a dataset. Or even use an invisible data listbox and copy the contents to a normal listbox.
Not as far as I know.
The standard is that you offer with the list a bunch of values in which 1 represents the current record.
Unless you have a multivalued field (against best practices) I can't see how you could multiselect...
Or what you might want is actually a sub-table?
DevExpress TcxDBListBox supports multiselect. I use their multiselect drop down check box bound to a database, it's sweet.
The components have methods you can implement to convert to and from your list; EditValueToStates and StatesToEditValue. While the data I store is not normalized (I store a semi-colon delimited list of version numbers), I created a full text search index on the field, with a semi-colon as a delimiter, and now I can still perform optimized searches on that field.
You could create your own custom listbox component that descends from TCustomListBox and add a Datasource property for your list, and another property such as TStrings to be used as a container to hold selected values. You could then post changes to your database using a button click.
If you fiddle with some of the options of a TDBGrid and restrict the columns it displays, you can make something that looks a whole lot like a listbox. Try setting the Options property to [dgTitles,dgTabs,dgRowSelect,dgAlwaysShowSelection,dgCancelOnExit,dgMultiSelect] and work from there.
In a TDbLookupListBox you have the option to bind two different things to data; first you can bind the list to a dataset (ListSource/ListField/KeyField), second you can bind the selected item to a field in another dataset (DataSource, DataField). There is nothing conceptually wrong with wanting to bind the list of items to a dataset, and then manually manage multiple selections, however I don't think it is possible with the current implementation without subclassing and enabling the required control styles.
Based on your comment to François, I would use a normal TListbox and write code to insert all distinct values into the list, and then handle the multi-select values yourself. Jeremy's solution also works, and the DevExpress Express Quantum Grid has a nice filter system which might even save you some other programming.

How can I copy a Delphi TTable including its calculated fields?

I have defined a Delphi TTable object with calculated fields, and it is used in a grid on a form. I would like to make a copy of the TTable object, including the calculated fields, open that copy, do some changes to the data with the copy, close the copy, and then refresh the original copy and thusly the grid view. Is there an easy way to get a copy of a TTable object to be used in such a way?
The ideal answer would be one that solves the problem as generically as possible, i.e., a way of getting something like this:
newTable:=getACopyOf(existingTable);
You can use the TBatchMove component to copy a table and its structure.
Set the Mode property to specify the desired operation. The Source and Destination properties indicate the datasets whose records are added, deleted, or copied. The online help has additional details.
(Although I reckon you should investigate a TClientDataSet approach - it's certainly more scalable and faster).
Let me propose several things:
Let us suppose that you want to make changes programmatically. You could then use DisableControls and EnableControls methods of the TTable to disallow screen updates during that time.
If you want to have two screens with the same data (f.e. to compare data during online changes), you could actually create the same screen twice, with the TTable object being on the screen itself. It will have the exact same configuration (but not carry over previously made changes on the first screen but read the data from the database). Changes made on one screen will not be automatically refreshed on the other.
Another way: Try using TDataSetProvider with TTable as Dataset (source) feeding a TClientDataSet. ApplyUpdates would feed back the changes to the TTable. Since the calculated fields are read only, they are not affected. (untested, but should work)
I believe that the second approach (TClientDataset) is probably the best method to use in this scenario. An alternative would be to use a memory table (kbmMemTable for instance). Either way, you would clone your original table and then after making your changes loop thru the memory version of your dataset and update your original table.
You should be able to select the table on the form, copy it using Ctrl-C, then paste it into any text editor. You will get the text version of the object's properties which you can then edit as needed. When you are done, select all the text again and you can copy it to the clipboard and paste it back onto a form.

Resources