Cascading combo boxes - getting the 'dependant' combo box linked to control source to update when moving between records - combobox

I am sure this question has an easy answer but I need to ask it. I have a historical records database and an events form to enter events, dates etc into an events table. (I would like to post a reduced version of the database but cant seem to do this on the is forum.) There are hundred of events (this is history) and, to reduce the number of options in entering data, an unbound combo box (ETCIDcbx) in the entry form EventF selects categories of events from another table (EventTypeT) containing event categories (ETCID) and event types (ETID) and the value in this combo box is then the value in a Query (FindEventTypefromCategoryluQ) which selects a subset of event types which appear in the combo box ETIDcbx (bound to field ETID in the EventT table). The problem is that the ETIDcbx does not update as I scroll through events so I don't know if the event type is correct. Another combo box is bound to the control ETID but it displays only the numbers (which are hard to keep track of) rather than the description of the event type. Can anyone help?

Related

WPF Data gird row selection change event from selection change event

I'm looking for a workaround to an issue I've noticed in a project that I'm working on. I have a datagrid bound to a list of a class I've created. Whenever a user types in a stock symbol in this datagrid I have the roweditending event set to pull in some information to populate the other fields in the datagrid.
This generally works fine, however I've noticed that the event doesn't fire in some instances. I'm going to try to explain this the best I can... upon data entry if I enter information into a cell and then click another row on the datagrid - focus doesn't shift to the new row but shifts to the row containing the cell I was just entering data into from that cell, then if I select another row or input element the row edit ending event doesn't appear to fire and as such my code to populate the rest of the row doesn't execute.
Everything else is fine, and it's a pretty subtle issue, only one path of input to make it happen, but I was wondering if anyone had run into this or found a work-around?
I've been looking at the selection change event, but I need to be able to grab the row that was selected. In the row edit ending event I use
Dim NewTrade As Trade = e.Row.DataContext
to get the instance of my class that the row represents and to add the additional fields.
I'm not sure I see a way to get the row and it's datacontext for the row I just shifted from using the SelectionChangedEventArgs.
Any ideas for a workaround?

How to quickly populate a combobox with database data (VCL)

I have read all sorts of documents across the web about what should be a farly common and painless implementation. Since I have found no consistent and slick reply (even the Embarcadero website describes some properties wrong!) I am going to post a "short" howto.
Typical, frequent use case: the developer just wants to painlessy show a couple of database-extracted information in a combo box (i.e. a language selection), get the user's choice out of it and that's it.
Requirements:
Delphi, more or less any version. VCL is covered.
A database table. Let's assume a simple table with an id and value fields.
A DataSet (including queries and ClientDataSets).
A DataSource linked to the DataSet.
A TDBLookupComboBox linked to the DataSource that shall show a list of values and "return" the currently selected id.
Firstly, we decide whether the sort order is the one we want or not and if all the items in that table must be shown. Most often, a simple ORDER BY clause or a DataSet index will be enough. In case it's not, we may add a sort_order field and fill it with integers representing our custom sort order. In case we want to show just some items, we add a visible or enabled field and use it in our SQL. Example:
SELECT id, value
FROM my_database_table
WHERE visible = 1
ORDER BY sort_order
I have defined visible as INTEGER and checking it against 1 and not TRUE because many databases (including the most used SQLite) have little support for booleans.
Then an optional but surprisingly often nice idea: temporarily add a TDBGrid on the form, link it to the same TDataSource of the TLookupComboBox and check that you actually see the wanted data populate it. In fact it's easy to typo something in a query (assuming you are using a SQL DataSet) and get no data and then you are left wondering why the TDBLookupComboBox won't fill in.
Once seen the data correctly show up, remove the grid.
Another sensible idea is to use ClientDataSets for these kinds of implementations: due to how they work, they will "cache" the few rows contained in your look ups at program startup and then no further database access (and slowdown and traffic) will be required.
Now open the TDBLookupComboBox properties and fill in only the following ones:
ListSource (and not DataSource): set it to the TDataSource connected to the DataSet you want to show values of.
ListField: set it to the field name that you want the user to see. In our demo's case it'd be the value field.
KeyField: set it to the field name whose value you want the program to return you. In our demo it'd be the id field.
Don't forget to check the TabOrder property, there are still people who love navigating through the controls by pressing the TAB key and nothing is more annoying than seeing the selection hopping around randomly as your ComboBox was placed last on the form despite graphically showing second!
If all you need is to show a form and read the TDBLookupComboBox selected value when the user presses a button, you are pretty much sorted.
All you'll have to do in the button's OnClick event handler will be to read the combo box value with this code:
SelectedId := MyCombo.KeyValue;
Where SelectedId is any variable where to store the returned value and MyCombo of course is the name of your TDBLookupComboBox. Notice how KeyValue will not contain the text the user sees onscreen but the value of the id field we specified in KeyField. So, if our user selected database row was:
id= 5
value= 'MyText'
MyCombo.KeyValue shall contain 5.
But what if you need to dynamically update stuff on the form, depending un the combo box user selection? There's no OnChange event available for our TDBLookupComboBox! Therefore, if you need to dynamically update stuff around basing on the combo box choices, you apparently cannot. You may try the various "OnExit" etc. events but all of them have serious drawbacks or side effects.
One possible solution is to create a new component inheriting from TDBLookupComboBox whose only task is to make public the hidden OnChange event. But that's overkill, isn't it?
There's another way: go to the DataSet your TDBLookupComboBox is tied to (through a DataSource). Open its events and double click on its OnAfterScroll event.
In there you may simulate an OnChange event pretty well.
For the sake of demonstration, add one integer variable and a TEdit box to the form and call them: SelectedId and EditValue.
procedure TMyForm.MyDataSetAfterScroll(DataSet: TDataSet);
var
SelectedId : integer;
begin
SelectedId := MyDataSet.FieldByName('id').AsInteger;
EditValue.Text := MyDataSet.FieldByName('value').AsString;
end;
That's it: you may replace those two demo lines with your own procedure calls and whatever else you might need to dynamically update your form basing on the user's choices in your combo box.
A little warning: using the DataSet OnAfterScroll has one drawback as well: the event is called more often than you'd think. In example, it may be called when the dataset is opened but also be called more than once during records navigation. Therefore your code must deal with being called more frequently than needed.
At this point you might rub your hands and think your job is done!
Not at all! Just create a short demo application implementing all the above and you'll notice it lacks of an important finishing touch: when it starts, the combo box has an annoying "blank" default choice. That is, even if your database holds say 4 choices, the form with show an empty combo box selected value at first.
How to make one of your choices automatically appear "pre-selected" in the combo box as you and your users expect to?
Simple!
Just assign a value to the KeyValue property we already described above.
That is, on the OnFormCreate or other suitable event, just hard-code a choice like in example:
MyCombo.KeyValue := DefaultId;
For example, by using the sample database row posted above, you'd write:
MyCombo.KeyValue := 5;
and the combo box will display: "MyText" as pre-selected option when the user opens its form.
Of course you may prefer more elegant and involved ways to set a default key than hard-coding its default value. In example you might want to show the first alphabetically ordered textual description contained in your database table or whatever other criterium. But the basic mechanic is the one shown above: obtain the key / id value in any manner you want and then assign it to the KeyValue property.
Thank your for reading this HowTo till the end!

DataGrid selected item changed more quickly than data loads from DB

We have the following WPF : a datagrid with row detail template. Selected line on this grid (customer selection) is handled to trigger two queries to retrieve address and contacts data, then row detail template show these data in two tabs, each with a datagrid. All the magic is done with binding, subgrids bind to properties of main Customer object, which we have as an IObservableCollection.
The bug happens when using “move down” arrow on keyboard, selected index changes fast, so fast than queries result “come back late” to interface, thus data is incoherent to what is shown and data update cannot perform. We have an exception. So my question is : how to prevent this in a proper manner ?
Should we have a try-catch the right kind of obscure exception then do nothing (loose data that cannot be applied to interface) ?
We don’t want to wait for data to come back, if user scrolls very quickly, customer selected line should go down and no matter details aren’t shown.
Maybe we should have a delay before selected item details are retrieved ? Thus no query if selected item changes before end of delay ?
Thank you for your ideas.
Gists for code:
Xaml : https://gist.github.com/Xarkam/3b89eb614124bb2f2307
Selected index changed handling : https://gist.github.com/Xarkam/cf28844ce05fd4984807
Edit 10th of July :
I have modified the main datagrid items definition as in following gist : (add https:// prefix, I don't have enough reputation to add more links, sorry) gist.github.com/postb99/d3be79f0ef2544d685f9 (inspired from stackoverflow.com/questions/13374957/datagrid-throws-invalidoperationexception-by-scrolling and proposed answer) but problem still persists...
We've solved it, if I remember well we didn't have observable collections for every collection of objects to display.

EditorGrid dynamic/dependant Combo Box Editor Fields

In ExtJS, say you have a simple editorgrid with two columns, X and Y, both of which have a combo box as their editor.
How is is possible to make the values available in the second column's combo box (Y) depend on the selected value in the first column (X)?
So, depending on what is selected in X, the values available in Y change accordingly.
Assuming that you have a store backing each combobox, you can use the store's filter or filterBy methods to narrow down the results of one combobox based on the other's current selection. You can call either filter function on the 'select' or 'change' events of the combobox. I've used this method successfully in the past, and it worked well for my needs. I had around 200 records per combobox, so I'm not sure how well this solution scales if you have a good deal records more than that.
Managed to answer my question:
As the EditorGrid in question uses a RowSelectionModel (not a CellSelectionModel), I was able to add the below to the beforeselect event of the editor combobox:
Ext.getCmp('EDITORGRID').selModel.getSelected().set('colY','myvalue');
Having updated the relevant store as necessary, myvalue was simply set to the first record returned.
Here is a working example from JoeKuan

Can't make ListItems Visible

I have 3 ListViews in a WPF Grid Column, one each for customers, products, and vendors.
They each get data from LINQs to separate SS2005 queries in the same DB.
The lists are populated when a user enters text in a TextBox.
The LINQ query code uses as startswith() function that reads the user entry and returns a smaller list to the ListView.
When the code is run, each ListView is populated properly.
However, the Products list doesn't display the entries.
I attached a call to a MessageBox to the selection event of the ListItem and the message box correctly displays the product.
The single difference between the two working lists and the bad one is the query that calls the bad one returns a single field from the DB and the others return two fields, a text field and an integer ID field.
I'm at a loss.
The background of the ListView is white, the foreground is black (they were set using the properties panel).

Resources