So in my previous post I was writing about how the issue with textHeight wasn’t returning the correct values if one tried to access it directly after setting a new size, font - you name it - to a TextArea. Even if one called the .invalidateNow()-function, as outlined in the documentation.
I also touched upon what, from reading the TextArea.as-file and tracing the functions called, I thought might be the reason and a solution to this issue.
This is due to the event internally in TextArea.as handling the change-event is catched under callLaterDispatcher(), which grabbes cue’d events. So the alteration of the textFields.textHeight (or any of the look of the component) is infact not done right away but cue’d. And as mentioned before, it doesn’t matter if ones uses .validateNow() as it seems fairly useless when it comes to actually validate (redraw) the component with the most up to date data as it doesn’t de-cue any instructions meant for it.
So…how to solve this? I suppose the only way would be to make sure our own instruction to set the correct height of a component is infact called in the same manner, as a delayed event in a cue of events.
So I went back and did just that.
As always with solutions that cater to a specific problem for a specific user (albeit in this case, many-many seemingly has issue with this) is that it might or it might not be universal. Well, I think most everyone will be able to use this angle, but it will require one to adjust the code around it some if one is counting on everything being done at an exact moment of the actual calling of an function. In an event and message based world that just isn’t happening a lot of the times.
So my solution basically became:
someTextArea.callLater(this.ImportantFunctionWithResize, new Array(someTextArea) );
where someTextArea is just the TextArea we just altered (in my case it was for instance setting fontFamily, size and others from a menu) and the function ImportantFunctionWithResize is your function where you handle the actual resizing of the TextAreas height, defined as for example
private function ImportantFunctionWithResize(inTextArea:TextArea):void
(All variable and function names are edited to simplify the reading and avoid issues with referring code written under contract.)
So the important thing is that our function is put in the message (event) cue, after the TextAreas own [Change]Event, to make sure the component has infact had all the values we supplied to it commited and properties updated.
{ 5 } Comments
A side-note regarding this when dealing with setting height of new TextArea components before they are added to a canvas [or similar] is that deferring the call to after the [change]Event still doesn’t fix the value as the component in itself still has the wrong value. This is because it has not yet been drawn and the process to calculate the textHeight and textWidth requires this. For instance: alter the application-scale and check textWidth and textHeight reported: it will be of [slightly] another value suddenly as it actually uses another, estimated, font-size to rescale it…
Anyway, to set height on create I therefor still use the hack-approach as outlined in my original post some time back. It’s really not that far off generally.
(You could always defer the calculation until after everything is loaded and drawn…some way. And then do a quick update for re-draw.)
Hopefully Adobe will fix this issue soon…pretty please?
You can always reference the internal TextField object of the TextArea which has it’s properties set properly (the bug is in the flex component). To do this you just have to reference the internal namespace. After your import statements you add a reference to the namespace:
import mx.controls.*;
use namespace mx_internal;
Then once you have set the .text or .htmlText property of your TextArea, call validateNow() on the area, and then check the .textHeight of the TextField. See snippet below:
myTextArea.htmlText = “cool“;
myTextArea.validateNow();
var height:Number = myTextArea.getTextField().textHeight; //the internal field has the property set properly
You will want to pad the height probably since this gives you the exact height of the text.
HTH.
One of my first tries was accessing the internal data but for some reason - and it was so many revisions of the code back that it would take a while to hunt down - it didn’t seem to work properly. But it’s possible it was from a silly reason of calling the .validateNow() after, instead of before, calling the .getTextField().textHeight
And the usual note when dealing with internal referencing from the docs is that they can break at any time as they aren’t ’supported’ in the same sense as the properties of the normal components are. So be vary.
But in the meantime, it will probably work.
Added: and the import should be:
import mx.core.mx_internal;
use namespace mx_internal;
I was also screwed up sometimes back with creating a dynamic textarea in flex that would fit in the text within. I created a custom textarea in mxml adding a validator and calling the validateNow() method on initialisation. It worked wonderfully. I have the example on my blog with the source. You can have a look.
http://codevil.in/?p=28
Greatings,
Thanks for article. Everytime like to read you.
Thanks
Elcorin
Post a Comment