Delphi ADO + bookmarks - delphi

I am currently developing an application that uses databases dynamically.
meaning it is designed to work with any db, at any time, and any structures.
my concern is that I wish to "tag" or bookmark certain records, therefore I will require to use the Filter property to do some searching, and in the end, I wish to delete the filter, and be able to search through the bookmarked records...
however it seems the bookmark only work as long as the filter was set on that specified filter, so if I choose my second bookmarked item, i receive a totally different record than I expected, i.e. I did a filter, and bookmarked the first record, when I delete my filter and go to bookmark #1 I still just go to record nr. 1.
is there any other way to do this ? or is it required to do this in a different way ?
hope someone here has some mad genuine solution to this :)

A dataset in Delphi can bookmark only one record. TDataset.BookMark is the placeholder for that bookmarked record. A bookmark made while dataset was filtered is valid after the filter is gone too. So if you filter your dataset, and bookmark a record, then remove the filter, and go to your bookmark record, you should get to the same record.
If you are not sure if your bookmark is still valid, specially when your dataset is being edited; then you can use TDataset.BookmarkValid method to validate your bookmark.
If you want to have a list of bookmarks (not just one bookmarked record), then you have to save them in a list or array. In Delphi 2009 and newer versions, TBookMark data type is defined as TBytes. In previous versions, TBookMark is defined as string. So if you are using a version of Delphi prior to Delphi 2009, you can use an instance of TStringList to save your list of bookmarks. If you are using Delphi 2009 and above, you can use an instance of TList generic type (declared in Generics.Collections unit) to store the list of bookmarks.
If you are using DBGrid, DBGrid has a property called SelectedRows which is of type TBookMarkList. You can use it to save a list of bookmarks from selected rows in the grid. You need to enable multi-select in DBGrid's options.

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 to implement a TDataSet-bound combobox with not-in-list values

I need to implement a combobox, which is bound to a TpFIBDataSet (descendant of TDataSet). I've done this several times before. It's not a big thing if it contains only predefined values.
This time, I'd like to have a combobox that accepts custom values entered by the user, also giving the ability to the user to select some predefined value. Newly entered values shall be inserted into some table of the database just before the record the combobox's field belongs to is posted.
The main problem seems to me, that predefined values are internally represented as integer IDs (the combobox I use is TwwDBComboBox from Roy Woll's InfoPower package, as it implements maplist functionality) because the field is a foreign key, while custom values may be nearly everything (only restricted by a mask).
How can I distinguish between an integer ID and integer user-input, for example?
See the set properties of the combobox:
AComboBox.Style := csDropDown;
AComboBox.MapList := True;
I don't request a solution as take this piece of code and be happy. I'm rather looking for some advice by others who might have or had a similar problem.
How can I distinguish between an integer ID and integer user-input, for example?
You go back to the database. Either query directly select count(*) from table where id = ComboBoxId.
Or use the Locate method of the dataset.
Or keep a cache handy in a MyList: TList<Integer> and do a MyList.BinarySearch to see if the item is already in the DB.
Obviously the cache will only work if the DB is single-user, because otherwise you will not be able to keep it up-to-date.
If it is not in the DB, you run the insert query.
After it's inserted you run the default combobox behavior, because now the values is sure to be in the DB.

Translating choice lists in LightSwitch

Is it possible to translate the display name of choice list items defined for LightSwitch entity properties?
I would like to be able to show different users translated display names in their language when they are viewing the SAME record, and also show translated names in the autocomplete combo box when they are editing a record.
Not really, no. The values are stored in the LSML file & aren't able to be modified at runtime. If you need to translate the values, then you'll need to use a lookup table (possibly by using a custom RIA service) instead of a Choice List.
The advantage of a Choice List is that it's very quick & easy to set one up.
The down side of a Choice List is the lack of flexibility, or even reusability (you have to define the values for the list every time you want to use it somewhere).

Custom Listbox: Limit Maximum Item Count

I have silverlight 3.0 project that has a listbox that is databound to a list of items. What I want to do is limit the number of items displayed in the listbox to be <= 10. I originally accomplished this by limiting the data bound to the list to 10 items by doing a .Take(10) on my orignal data and databinding the result.
The problem w/ the .Take(10) approach is that the original datasource may change and since .Take() returns a reference (or copy not sure) of the original data I sometimes do not see changes in the data reflected in my UI.
I'm trying to figure out a better way of handling this rather than the .Take() approach. It seems you shouldn't 'filter' your data using LINQ functions if you have more than one UI element bound to the same data. My only thought on how to do this better is to make a custom container that will limit the count, but that seems like it might be a mountain of work to make a custom stackpanel or equivalent.
Take(10) does not make a copy, it just appends another step to the LINQ query. But all execution is still deferred till someone pulls the items of the query.
If you were setting the items statically, a copy would indeed be created, by running the query once. But since you set the constructed query as the ItemsSource property of the list box, it can run and update it any time, so it is the right approach.
The real reason why you sometimes do not see changes in the data reflected in the UI is that the list box has no way to determine why the data returned by the query have changed and it surely doesn't want to keep constantly trying to refetch the data and maybe update itself. You need to let it know.
How can you let it know? The documentation for ItemsSource says that "you should set the ItemsSource to an object that implements the INotifyCollectionChanged interface so that changes in the collection will be reflected (...).". Apparently the default way of doing things by .Net itself does not work in your case.
So there are some examples how to implement that yourself e.g. in this SO answer. If even the top-level source data collection (over which you are doing the LINQ query) does not support these notifications (which you would just forward), you might need to update the list box manually from your other code which changes the underlying data.

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.

Resources