I have a busy function taking several of seconds to be executed. At the top of the function I set my busy indicator to be visible and at the bottom I set it to be invisible. But the display does not change while the function is executed and the busy indicator does not appear. here is the code structure
public function busyFunction():void{
busyIndicator.visible = true;
doStuff .. // takes several seconds
busyIndicator.visible = false;
}
i just want the first line (busyIndicator.visible = true) to be executed while the doStuff is on progress.
Regards
The problem is because Flex doesn't redraw the screen while your method is executing. So you make the busy indicator visible, do stuff, and hide the indicator. But no update to the screen will have occurred yet, so the indicator never shows.
The Flex "elastic racetrack" paradigm basically says that your app consists of two cycles, one where your logic is executed, and another where the screen is updated. So you need to set the busy indicator to visible, and then make it go away after at least one update cycle later.
To make this sort of thing work, you need to execute doStuff() asynchronously. Some code that does this might look like this:
private function startDoingStuff():void
{
busyIndicator.visible = true;
// dispatch "doStuff" event
dispatchEvent(new Event("doStuff"));
return; // returning here is important don't do stuff here
}
private function onDoStuff(e:Event):void
{
// do stuff;
busyIndicator.visible = false;
}
Another idea is to use Flex's callLater() method to execute your doStuff() method. This should guarantee that doStuff() is executed on the next cycle:
private function startDoingStuff():void
{
busyIndicator.visible = true;
callLater(onDoStuff);
}
private function onDoStuff():void
{
// do stuff
busyIndicator.visible = false;
}
Related
I need to know the value from which a combo box is changing, when it changes. I've been all through the documentation for the events, and none of them let you know what the value is before user interaction changes it. (currentStateChanging is a total red herring!) Handling the open event and saving the value isn't a solution, because there are other ways to change the value.
I'm using the Flex 3.5 SDK.
Something like this?
var currentVal : Object;
private function onChange(newVal) : void {
// currentVal stores your previous value - do something with it
currentVal = newVal;
}
<mx:ComboBox change="onChange(event.target.selectedItem)"/>
I just used the "changing" event on a Spark ComboBox to solve this very problem but it's not available on the mx version
Also - see this
I've come to the conclusion that there isn't an answer :( The best workaround is to override all possible ways there are to set the value of a combo box, plus handle any events that involve the user changing the value, back up that value and then you have a trail of previous values. Then, put a lot of comments saying
this is a 3.5-necessary kluge! If doing this on another SDK you might have to change it!
=======
I've come up w/a solution, but it's not perfectly reliable (since it makes assumptions about how it will work in other SDKs) and its elegance is wanting:
<mx:ComboBox xmlns:mx="http://www.adobe.com/2006/mxml" valueCommit="OnChangeAnyway()" change="OnChange()">
<mx:Metadata>
[Event(name='traceable_change', type="assets.LineComboChangeEvent")]
</mx:Metadata>
<mx:Script><![CDATA[
public static const CHANGE:String = 'traceable_change';
private var m_oOld:Object;
private var m_oNew:Object;
private var m_bCallLaterPending:Boolean = false; //This is necessary, because I found OnChangeAnyway() could be called any number of times before OnChange() is
private function OnChange():void {
var oNewEvent:LineComboChangeEvent = new LineComboChangeEvent(CHANGE, m_oOld); //there's nothing special in this class
dispatchEvent(oNewEvent);
}
private function OnChangeAnyway():void {
if (!m_bCallLaterPending) {
m_bCallLaterPending = true;
callLater(function ():void { m_bCallLaterPending = false;}, []); //presumably, whatever is passed to callLater() will be executed after everything else currently queued
m_oOld = m_oNew;
m_oNew = value;
}
}
]]></mx:Script>
m_oNew is obviously redundant because that value will be available to whatever handles traceable_change, but it does explain why I have to barrel-shift these objects.
There are a couple of reasons why I don't consider this reliable:
It assumes that the valueCommit handler will be called ahead of the change one. On my system, it always seems to, but I don't see that promise anywhere.
It assumes that whatever callLater() calls will be called after change is. See concerns for 1.
What I want to do is display a set of pages setting up a test. Once all the details are correct the user presses Commit and the next wizard page is displayed that I want to immediately run a series of tests in. Displaying those to the user and once complete the user can then click Next.
I know to disable Next is simply a case of returning false on isComplete() and that is implemented okay. So, I want to use the function that is called just after the widget is displayed and so I used showEvent() which was indicated to me as the function to use.
At the moment my test is just displaying a progress bar as a test hence using a timer.
void RunTestWizardPage::showEvent(QShowEvent *event)
{
ui->statusEdit->setText("Running Tests");
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(100);
}
void RunTestWizardPage::update()
{
static int i = 10;
ui->statusEdit->append("Running Tests...");
ui->testProgress->setValue(i++);
if(i == 100)
{
i = 0;
timer->stop();
complete = true;
emit completeChanged();
}
}
However this function appears to be called twice (and I think before the widget display although that may be a trick of my debugging) and as such it causes issues with the timer I think as the timer never ends. I did read in the docs about spontaneous events but from what I can see both calls to the function are not spontaneous.
Is it being called twice intentional and if so how do I stop it or is there another function to use?
Thanks!
There is QWizardPage::initializePage() which is called just before showing the page.
I have a problem, I have to stop one Loading class on button click. I already checked some forums related to this. But didn't find an exact solution.
For example:
Public Sub LoadDropDown()
Dim it As Integer
For it = 0 To 1000000
DropDownList1.Items.Add(it)
Next
End Sub
I have to load the DropDown on Load button click and I have to cancel that on cancel button click.
Since populating the control happens on the server, I can't imagine way to interrupt your method from the client. The whole control is being populated, rendered, and only then sent to the client. You might interrupt the callback using ajax, but then the control wouldn't be returned at all.
An alternative could be to load the contents in chunks with ajax and append them to the control on the client-side.
There is no formal way to do what you're asking, but you should be able to achieve the same result if you refactor your code. If certain content shouldn't be loaded for certain users, do that logic in your code behind before it renders to the page.
Per your response to the other answers...
You could consider doing multiple my_ddl.items.add() calls on a timer. Would involve multiple, separate postbacks / ajax calls. For example:
1) add records for 2 seconds (instead of a fixed number of records at a time)
2) check for session("continue") = "true"
3) add more records for 2 more seconds
4) check session("continue")
...
At some point, user clicks cancel, which assigns "false" to session("continue"). Next time your loop checks session("continue"), it will see that it's false and exit.
This would give you a partially-loaded data control. You might want other code to wipe-out the partial update.
I think you could accomplish this with a Session Variable. Forgive me, but I'll have to provide the example in C#, but I'm sure you can get the general idea of this.
private bool CancelRequested
{
get
{
if (Session["CancelRequested"] == null)
return false;
else
return (bool)Session["CancelRequested"];
}
set
{
Session["CancelRequested"] = value;
}
}
public void LoadDropDown()
{
for (int it = 0; it <= 1000000; it++)
{
if (CancelRequested)
{
CancelRequested = false;
break;
}
//Your logic here
}
}
protected void btnCancelRequest_Click(object sender, EventArgs e)
{
CancelRequested = true;
}
The idea here is that the inital loop checks a Session variable to see if it should continue or break out of the loop. If you have a button on the page that will allow the user to set this Session variable to "true", they can essential communicate to the inital request and cause it to break out of the loop. I'm not sure if this would fully accomplish what you're looking to achieve, but hopefully it helps.
In an AIR application I have the following code:
theDialog = PopUpManager.createPopUp( this, TheDialogClass, true ) as TheDialogClass;
theDialog.addEventListener(FlexEvent.CREATION_COMPLETE, cpuIntensiveCalc);
At the end of cpuIntensiveCalc the dialog is removed. The dialog informs the user that "something is going on, please stand by."
The problem is that cpuIntensiveCalc starts before the dialog draws. So the user experience is that the application freezes for 10 seconds with no indicator, then the modal dialog flashes quickly (less than a second) on screen.
The Adobe docs say this about creation_complete
Dispatched when the component has finished its construction,
property processing, measuring, layout, and drawing.
So this feels like the correct event.
In the name of completeness, I also tried
theDialog = PopUpManager.createPopUp( this, TheDialogClass, true ) as TheDialogClass;
cpuIntensiveCalc();
But had the same results.
TIA
The reason for this is that the Flash Player is single threaded, and so you are blocking the UI from reacting to the Dialog Popup until the maths chunk is finished.
Hacky fix time...
You have two options.
(This one should work, but is untested) Wrap the cpuIntensiveCalc() call in a callLater, so that the UI can finish rendering before you block the rendering.
Or
Use "Green Threads" to break up your processing so that you don't completely block the UI processing. Take a look.
(I just had the same issue => even if this thread is old, I just wanted to contribute my solution)
(disclaimer: this is a bit ugly, but they say that's ok in the UI layer... ;-) )
Flex is single threaded (at least from our developer's perspective, I think behind the scene threads are used by the VM)
=> you typically execute your code in the UI thread, after the user did some action on a widget. Any call to update a UI component (like setProgress or setLabel) will only be rendered on screen at the end of the render cycle (see again UiComponent life cycle).
=> In therory calling "cpuIntensiveCalc" in a callLater will let the framework display your popup before executing the method.
In practice though, I noticed you typically have to have for a couple of UI cylces before the popup be displayed, like this:
new MuchLaterRunner(popup, 7, cpuIntensiveCalc).runLater();
MuchLaterRunner being defined like this:
public class MuchLaterRunner
{
var uiComp:UIComponent;
var currentCounter = 0;
var cyclesToWaitBeforeExecution=0;
var command:Function;
public function MuchLaterRunner(uiComp:UIComponent, cyclesToWaitBeforeExecution:uint, command:Function)
{
this.uiComp = uiComp;
this.command = command;
this.cyclesToWaitBeforeExecution =cyclesToWaitBeforeExecution;
}
public function runLater() {
currentCounter ++;
if (currentCounter >= cyclesToWaitBeforeExecution) {
uiComp.callLater(command);
} else {
// wait one more cycle...
uiComp.callLater(runLater);
}
}
}
The issue is the same when calling setProgress afterward: we must divide cpuIntensiveCalc into small callable methods that can be ran at each UI cycle, otherwise the progressbar won't, err, progress.
Use enterFrame event on popup. Don't forget to remove the listener in the enterFrame event handler - otherwise the cpu intensive method will be called in each frame, crashing your app. If this doesn't work at first, use a private number as a counter and keep incrementing it in the enter frame handler - call cpu heavy method only when the counter reaches the appropriate value. Find the 'appropriate' value by trail and error.
theDialog = PopUpManager.createPopUp(this, TheDialogClass, true) as TheDialogClass;
theDialog.addEventListener(Event.ENTER_FRAME, onEnterFrame);
private function onEnterFrame(e:Event):void
{
//can use theDialog instead of e.currentTarget here.
(e.currentTarget).removeEventListener(Event.ENTER_FRAME, onEnterFrame);
cpuIntensiveCalc();
}
//in case the above method doesn't work, do it the following way:
theDialog.addEventListener(Event.ENTER_FRAME, onEnterFrame);
private var _frameCounter:Number = 0;
private function onEnterFrame(e:Event):void
{
_frameCounter++;
var desiredCount:Number = 1;//start with one - increment until it works.
if(_frameCounter < desiredCount)
return;
//can use theDialog instead of e.currentTarget here.
(e.currentTarget).removeEventListener(Event.ENTER_FRAME, onEnterFrame);
cpuIntensiveCalc();
}
I have two functions in an ActionScript class, they are:
private function loaderCompleteHandler(event:Event):void
{
_loader = Loader(event.target.loader);
selectedBitmap = Bitmap(_loader.content);
}
public function byteArrayToBitmap( byteArray:ByteArray ):void
{
_loader.contentLoaderInfo.addEventListener( Event.COMPLETE, loaderCompleteHandler );
_loader.loadBytes( byteArray );
}
Is it possible to send the selectedBitmap variable back to the byteArrayToBitmap function after the event completed?
Not clear what you want to do.
You cannot return anything from the same call stack as the original call to byteArrayToBitmap, and there's no "sleep" available in AS3. Once you get into "loadCompleteHandler", you cannot return anything to the caller of byteArrayToBitmap. So you'll have to modify the caller to wait for the event COMPLETE and then check for the selectBitmap object. This will have to be stored somewhere.
That is, if I understand your problem.