Forcing ComboBox Component Open Direction in Flex
Tuesday, October 14th, 2008
The ComboBox component is neat because it automatically determines if there is enough space for it to open down and if there isn’t, it’ll tween upwards instead. However, I came across an issue recently where I needed to force it to open up or down. Unfortunately, the displayDropdown method is private, so overriding and re-writing the code is out of the question. You could override keyDownHandler, close, open, and downArrowButton_buttonDownHandler and then replace displayDropdown with a custom function in each, but that seems annoying to me! I’m not sure why the displayDropdown function is private, but anyway, here’s a simple solution to force direction.
First navigate to the Flex framework codebase (mine is in /Applications/Adobe Flex Builder 3/sdks/3.0.0/frameworks/projects/framework). What you’re going to do is copy the ComboBox source file and it’s required classes/files to your local classpath. This will force flex to use mx.controls.ComboBox in your classpath instead of the Frameworks. I found that I needed to copy over the following files (core.Version and a number of the files from styles/metadata are “included” in ComboBox.as). Don’t worry, you’ll only have to modify ComboBox.
/mx
/controls
/ComboBox.as
/core
/Version.as
/styles
/metadata
Once you’ve added these to your class path, open up ComboBox. First add some hacky public vars to the class:
public var forceDirectionDown:Boolean = false; public var forceDirectionUp:Boolean = false;Then in the definition for
displayDropdown, starting at around line 1553, change the method to look something like this (I didn’t rewrite much, just hacked in the new stuff because I needed this quickly):
var pt:Point = new Point(point.x, point.y);
// if we donot have enough space in the bottom display the dropdown
// at the top. But if the space there is also less than required
// display it below.
if (point.y + _dropdown.height > screen.height &&
point.y > _dropdown.height)
{
// Dropdown will go below the bottom of the stage
// and be clipped. Instead, have it grow up.
point.y -= (unscaledHeight + _dropdown.height);
initY = -_dropdown.height;
tweenUp = true;
}
else
{
initY = _dropdown.height;
tweenUp = false;
}
// hack in the force support
if (forceDirectionDown) {
// reset to point.y
point.y = pt.y;
initY = _dropdown.height;
tweenUp = false;
} else if (forceDirectionUp && !tweenUp) {
point.y -= (unscaledHeight + _dropdown.height);
initY = -_dropdown.height;
tweenUp = true;
}
That’s pretty much it. Now when you declare your combo in mxml, you can specify to force it up or down. Example:
<mx:ComboBox id="mapType"
forceDirectionUp="true"
labelField="label"
labelFunction="labelFunction"
visible="{ready}"
width="150"/>
Got a better way of doing this? Show me!!
Damien says:
November 14th, 2008 at 7:57 am
Andrew says:
January 6th, 2009 at 1:22 pm
// add these vars toward the top of of the class definition
var __forceOpenDown:Boolean = false;
var __forceOpenUp:Boolean = false;
// add these getter/setter functions
function get forceOpenUp() : Boolean
{
return __forceOpenUp;
}
function set forceOpenUp(bool:Boolean) : Void
{
if ( bool == undefined ) bool = true;
__forceOpenUp = bool;
}
function get forceOpenDown() : Boolean
{
return __forceOpenDown;
}
function set forceOpenDown(bool:Boolean) : Void
{
if ( bool == undefined ) bool = true;
__forceOpenDown = bool;
}
Then where you reworked your code (for AS 2.0 around line 530 of ComboBox.as) - after the line "var endVal;"....
var openUp:Boolean = point.y + dd.height > Stage.height;
if ( openUp ) {
if ( __forceOpenDown ) openUp = false;
} else {
if ( __forceOpenUp ) openUp = true;
}
And the line "if (point.y+dd.height > Stage.height )" (should be right after), just change to "if (openUp)" ...instead of "point.y+....Stage.height" (you're just changing the condition here.
then in your actual code for w/e, just use (forceOpenDown or forceOpenUp):
trace(my_cb.forceOpenDown);
my_cb.forceOpenDown = true;
trace(my_cb.forceOpenDown);
radley says:
January 27th, 2009 at 11:31 am
Craig Grummitt says:
March 3rd, 2009 at 6:04 pm
i simplified your code a little though - after adding forceDirectionUp and forceDirectionDown boolean vars, i just changed the if statement:
if (forceDirectionUp || (!forceDirectionDown &&
point.y + _dropdown.height > screen.height &&
point.y > _dropdown.height))
There is still a problem, however - when I set the Combo to 'forceDirectionDown' and there isn't enough room at the bottom of the screen, when the dropdown closes, it assumes to animate the dropdown down, away from the combo. To resolve this, I added to the section a few lines below where we were editing, that starts 'closing the dropdown', where it sets endY. I changed the condition line to:
endY = (forceDirectionUp || (!forceDirectionDown &&
(point.y + _dropdown.height > screen.height || tweenUp))