I found only a few references to this, but not much of a well documented solution, so for those who stumble upon this needing one, you’re in luck.

The problem: The ComboBox does not dispatch a change event under all circumstances, most notably when you use the keyboard to select find the particular item in the list. Think of a states combo, where by ‘n’ will first cycle to Nebraska. This then closes the combo, but does not appropriately fire the change event as it would if you manually scrolled and selected ‘Nebraska’ from the list.

Why the problem exists: The combo is a composition of a TextInput, a Button, and a List. While the appropriate change event listener is added to the input, it’s not added to the List to it’s fullest extent. It only checks to see if non-printing characters are used to see if the user is attempting to scroll the list with the arrow or page-up / page-down keys. Thus, when using a normal alpha-numeric filter,¬† this doesn’t reflect itself in the parent combo control.

The fix: This fix is unusually difficult in light of the fact of one of my earlier rants that at best all properties and / or methods of a highly public and distributed platform, ie the SDK should only ever be protected. Literally all accessors to both the instance list and it’s combo listeners are private? What the hell?

Further proof of their own exacerbation, is a comment that literally says starting around Line 1512:

private function displayDropdown(show:Boolean, trigger:Event = null):void

// Subclasses may extend to do pre-processing
// before the dropdown is displayed
// or override to implement special display behavior

So remind me, how is that possible with a private selector? Oh yeah.

So the only way around this is to override the downArrowButton_buttonDownHandler() handler, and use the mx_internal::isShowingDropdown property to find if the list is about to be shown, and get a reference to the list by grabbing the dropdown property where you will attach your own ListEvent.CHANGE listener. In this handler you proxy dispatch a clone from the ComboBox point of view, and you finally have a change event that now works.

Finally, create another Event.REMOVED_FROM_STAGE event in the createChildren() method, since destroyDropdown() is also private (note to self: !#$#@!) to clean up your additional change event to prevent  a memory leak.