Spark TextInput incomplete value using Barcode Reader - apache-flex

working with Flex4 AIR app, using this component I get data from the barcode reader,
<s:TextInput id="barcode" enter="showBarcode()"/>
Then for handle the enter event, wich is automatically triggered when reader finishes its reading
private function showBarcode():void{
Alert.show(barcode.text);
}
Ok, very simple. But my problem is: the text showed in the Alert Box is incomplete, it misses one or two digits (last) or it just shows the entire text in the TextInput component (correct view)
I already test my BC reader using the notepad, and it's fine.
I have tested same code using MX components in Flex3 web app, and there's no problem.
Some help will be appreciated.
Tnx

We ran into the same problem and we ended up saving the chars received from the barcode scanner in a buffer instead of accessing the TextInput text property. We always received the chars in the correct order but, with the Spark TextInput, the text property was sometimes scrambled.
<s:TextInput id="barcode" keyDown="barcode_keyDownHandler(event)" />
This is the buffer we used and these are the relevant functions:
private var textBuffer:ArrayList = new ArrayList();
protected function getTextBufferContent():String
{
var content:String="";
for (var i:int = 0; i < textBuffer.length; i++)
{
content=content.concat(textBuffer.getItemAt(i));
}
return content;
}
protected function handleKeyboardEnter():void
{
var barcodeScan:String=getTextBufferContent();
textBuffer.removeAll();
if (barcodeScan != "")
{
Alert.show(barcodeScan);
}
}
protected function barcode_keyDownHandler(event:KeyboardEvent):void
{
trace("barcode_keyDownHandler: " + event.keyCode + " " + String.fromCharCode(event.keyCode));
if (event.keyCode == Keyboard.ENTER)
{
handleKeyboardEnter();
}
else textBuffer.addItem(String.fromCharCode(event.keyCode));
}
The mx:TextInput also worked, but we wanted to keep the Spark component.

Instead of using "enter" try listening to the onKeyUp event.
You'll have to figure out the appropriate end of line character read in from the bar code scanner. But, just key in on that and do your alert that way; otherwise do nothing.

Is text at least displayed as you would expect in Spark TextInput by using the same workflow with barcode reader as shown in this video "Flash Nicey Nice With HID" http://www.youtube.com/watch?v=sEw0RB-Uj00 ?
Anyway, I do not think Adobe Flash player supports officially other HID devices rather then keyboard or mouse.
Thus I would not expect Adobe engineers to make Spark TextInput compliant with your barcode reader input any time soon.

We ran into this same thing where I work, with barcode scanning in Flex 4 / Spark. There are two workarounds we found.
The first is to use the old "Halo" TextInput control (<mx:TextInput />). You can do this even in a Flex 4 project.
The second method, is to keep the "Spark" TextInput control, but to use a very short Timer in your keyUp or keyDown event to let the text property be fully filled with the barcode scan data -- I don't know exactly why this works, but it does. And you get to keep all of the benefits of the Spark control/skinning. So the original poster's example could be fixed if it became:
<s:TextInput id="barcode" keyDown="showBarcode(event)"/>
and
private function showBarcode(evt:KeyboardEvent):void {
var t:Timer = new Timer(10, 1); // 10ms
t.addEventListener(TimerEvent.TIMER, function():void {
Alert.show(barcode.text);
});
t.start();
}
Bonus:
If you didn't want to use this Timer all over your code in event handlers, you could make a utility function for it in a Barcoder.as class like so:
public static function checkScan(evt:KeyboardEvent, callback:Function):void
{
if (evt.keyCode == Keyboard.ENTER)
{
var t:Timer = new Timer(10, 1); // 10 ms
t.addEventListener(TimerEvent.TIMER, function():void { callback(); } );
t.start();
}
}
Then, if you had two separate barcode fields, with two separate handlers (bc1_handler() and bc2_handler()) your mxml would be something like:
<s:TextInput id="barcode1" keyDown="Barcoder.checkScan(event, bc1_handler)" />
<s:TextInput id="barcode2" keyDown="Barcoder.checkScan(event, bc2_handler)" />

Related

Removing a blinking cursor in Flex Flash Player

I have a MX TextInput field on my form. As one of our user has seizure problems with blinking cursors, I am trying to disable it but without success. Through Control Panel, I have been able to prevent a blinking cursor in Office Apps and on the Web browsers but not with the Flex Application which uses the Flash Player. Has anyone come across this issue and have a solution?
Here's a simple solution that removes the cursor all together. I'm not sure if you want to remove the cursor (feasible) or stop the cursor from blinking (seems less feasible).
It works by setting the underlying TextField object's selectable property to false. The MX TextInput class has it's own selectable property, however, the code in TextInput also requires the editable property to be false to disable selection. So you need to extend TextInput to work around that.
The underlying TextField doesn't expose any properties to stop the cursor from blinking (that I'm aware of). TextField is one of Flash Player's built in classes, so the chances of modifying this low level behavior seem slim.
This obviously breaks the ability to copy/paste in the TextInput. You might have to devise a way to temporarily enable selection to support copy/paste or selecting text in general.
package
{
import mx.controls.TextInput;
public class CustomTextInput extends TextInput
{
public function CustomTextInput()
{
}
private var _hideCursor:Boolean = true;
private var hideCursorChanged:Boolean = true;
public function get hideCursor():Boolean
{
return _hideCursor;
}
public function set hideCursor(value:Boolean):void
{
if (value == hideCursor)
{
return;
}
hideCursorChanged = true;
_hideCursor = value;
invalidateProperties();
}
override protected function commitProperties():void
{
super.commitProperties();
if (hideCursorChanged)
{
hideCursorChanged = false;
textField.selectable = !_hideCursor;
}
}
}
}

Flex: Restore Spark VideoDisplay stream

EDIT
If someone can at least tell me how to receive an event when the streams disconnects that would be great.
The documentation for this control is simply horrible. I have an application that will have a live video stream and I'm looking for a way to make the VideoDisplay control restore its connection in case of the occurrence of any of these specific scenarios:
The application starts and the stream is not online yet.
The application is streaming and the user is disconnected from the internet.
The application is streaming and the video server crashes and reboots.
I'm using Wowza Media Server and Wirecast to test this. 1 and 3 don't work, I'm not sure number 2 does. I made number 1 work by adding this very questionable piece of code:
protected function onMediaPlayerStateChange(event:MediaPlayerStateChangeEvent):void
{
if (event.state == MediaPlayerState.PLAYBACK_ERROR)
{
var videoSource:DynamicStreamingVideoSource = this.videoDisplay.source as DynamicStreamingVideoSource;
try
{
this.videoDisplay.source = null;
this.videoDisplay.source = videoSource;
}
catch (any:*) {}
}
}
As you can see I need a try/catch block since both calls to source cause exceptions, yet whatever happens before those exceptions seems to fix problem #1. This doesn't fix problem #3 because a media state change event apparently doesn't occur when you stop the video server.
This is my control declaration:
<s:VideoDisplay id="videoDisplay" click="onVideoStreamClick(event)" mediaPlayerStateChange="onMediaPlayerStateChange(event)" muted="{this.videoMuted}" top="10" width="280" height="220" autoPlay="true" horizontalCenter="0">
<s:source>
<s:DynamicStreamingVideoSource id="videoSource" streamType="live" host="{FlexGlobals.topLevelApplication.parameters.videoStreamURL}">
<s:DynamicStreamingVideoItem id="videoItemLow" streamName="{FlexGlobals.topLevelApplication.parameters.videoLow}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoLowBitrate}" />
<s:DynamicStreamingVideoItem id="videoItemMedium" streamName="{FlexGlobals.topLevelApplication.parameters.videoMedium}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoMediumBitrate}" />
<s:DynamicStreamingVideoItem id="videoItemHigh" streamName="{FlexGlobals.topLevelApplication.parameters.videoHigh}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoHighBitrate}" />
</s:DynamicStreamingVideoSource>
</s:source>
</s:VideoDisplay>
Does anyone know how to make the VideoDisplay recover from these issues? Any help is appreciated, thanks.
If anyone has this problem, this is how I solved it. You need to set the video source to a blank image in order to stop the streaming, otherwise it will never, ever stop. This solution works for all scenarios described above:
private function resetVideo():void
{
//save current source object
this.videoEventsDisabled = true;
var videoSource:DynamicStreamingVideoSource = this.videoDisplay.source as DynamicStreamingVideoSource;
try //switch to blank image, only this will stop the video stream
{
this.videoDisplay.source = "assets/images/video_offline.png";
}
catch (any:*) {}
//wait a few seconds and reset video source
setTimeout(resetVideoSource, 2000, videoSource);
}
private function resetVideoSource(videoSource:DynamicStreamingVideoSource):void
{
this.videoEventsDisabled = false;
this.videoDisplay.source = videoSource;
}
protected function onMediaPlayerStateChange(event:MediaPlayerStateChangeEvent):void
{
if (this.videoEventsDisabled)
{
return;
}
//something went wrong
if (event.state == MediaPlayerState.PLAYBACK_ERROR)
{
resetVideo();
}
}
protected function onCurrentTimeChange(event:TimeEvent):void
{
if (this.videoEventsDisabled)
{
return;
}
//if there was a number before, and its suddendly NaN, video is offline
if (isNaN(event.time) && !isNaN(this.previousVideoTime))
{
resetVideo();
}
else //store event time for future comparisons
{
this.previousVideoTime = event.time;
}
}
MXML:
<s:VideoDisplay id="videoDisplay" click="onVideoStreamClick(event)" mediaPlayerStateChange="onMediaPlayerStateChange(event)" currentTimeChange="onCurrentTimeChange(event)" muted="{this.videoMuted}" top="10" width="280" height="220" autoPlay="true" horizontalCenter="0">
<s:source>
<s:DynamicStreamingVideoSource id="videoSource" streamType="live" host="{FlexGlobals.topLevelApplication.parameters.videoStreamURL}">
<s:DynamicStreamingVideoItem id="videoItemLow" streamName="{FlexGlobals.topLevelApplication.parameters.videoLow}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoLowBitrate}" />
<s:DynamicStreamingVideoItem id="videoItemMedium" streamName="{FlexGlobals.topLevelApplication.parameters.videoMedium}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoMediumBitrate}" />
<s:DynamicStreamingVideoItem id="videoItemHigh" streamName="{FlexGlobals.topLevelApplication.parameters.videoHigh}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoHighBitrate}" />
</s:DynamicStreamingVideoSource>
</s:source>
</s:VideoDisplay>
As a variant you may handle NetStream.Play.PublishNotify from NetStream object.
var _source:DynamicStreamingResource;
_source = new DynamicStreamingResource("rtmp://...", StreamType.LIVE);
var streamItems:Vector.<DynamicStreamingItem> = new Vector.<DynamicStreamingItem>();
streamItems.push(new DynamicStreamingItem(streamName, 0));
_source.streamItems = streamItems;
_rtmpDynamicStreamingNetLoader = new RTMPDynamicStreamingNetLoader();
_rtmpDynamicStreamingNetLoader.addEventListener(LoaderEvent.LOAD_STATE_CHANGE, rtmpDynamicStreamingNetLoaderStateChangeHandler);
var cvp:VideoDisplay = new VideoDisplay();
_source.mediaType = MediaType.VIDEO;
var videoElement:MediaElement = new VideoElement(_source, _rtmpDynamicStreamingNetLoader);
cvp.source = videoElement;
private function rtmpDynamicStreamingNetLoaderStateChangeHandler(event:LoaderEvent):void
{
var netStream:NetStream = event.loadTrait["netStream"] as NetStream;
if (netStream != null && !netStream.hasEventListener(NetStatusEvent.NET_STATUS)) {
netStream.addEventListener(NetStatusEvent.NET_STATUS, netStreamNetStatusHandler, false, 0, true);
}
}
private function netStreamNetStatusHandler(event:NetStatusEvent):void
{
if (event.info.code == "NetStream.Play.UnpublishNotify") {
}
if (event.info.code == "NetStream.Play.PublishNotify") {
}
}
I used Johnatan's code along with JayPea's idea to solve my problem!
I used all that Johnatan gave, with the following small change in the netStreamNetStatusHandler():
private function netStreamNetStatusHandler(event:NetStatusEvent):void
{
trace(event.info.code);
if (event.info.code == "NetStream.Play.PublishNotify")
{
try
{
cvp.source = ''; //Doesn't need to be a valid path. Empty string should suffice.
}
catch (error:Error)
{
trace('Video source error: ' + error.message);
}
cvp.source = videoElement;
}
}
This resets the source when the stream stops and restarts, thus causing the video display to play again.
NOTE: The video display's autoPlay property needs to be set as 'true'.
I know this question was asked many years ago, but it still helped me solving my problem encouraging me diving into the sources of VideoDisplay (since there obviously is no "clean" solution ;-(
And for those who have the same problem my "solution" ,ight be helpful too:
I had to restart the same video after the user clicked abort but then decided to play it again. Without "resetting" VideoDisplay this resulted in a black screen.
Here's how I solved the issue after I found out that in VideoDisplay an internal function "setupSource()" was called when in the commitProperties() function no thumnailSource was defined.
Since the setupSource() function was not publicly accessible I used this:
myVideoDisplaySpark.enabled = false; // changes properties
myVideoDisplaySpark.source = ""; // does not harm ;-)
myVideoDisplaySpark.mx_internal::thumbnailSource = null; // forces a setupSource in commitProperties
myVideoDisplaySpark.enabled = true; // finally results in the call if setupSource
That's at least what I understood; and at the end it worked for me ;-)

Pasting a large amount of text in a TextArea leads to a script execution timeout

(Flex 3) I have a TextArea component which needs to hold the user's clipboard content. The TextArea does the job relatively well for most cases but when pasting a large amount of data, I can't seem to get the content in the component at all due to the script execution timeout.
I've done a fair deal on investigation to try and hopefully find how I could make this work.
I found that the TextArea is using a IUITextField (which is in my case an instance of TextField at runtime) do handle the job of obtaining the pasting data and then throws an event when it is done.
I have found no way to take a look at the source of TextField as it is a class in the playerglobal.swc library.
Is there a way for me to maybe see the source of that class or is there something I'm missing in my approach to figure out a way to make this work?
P.S: I know there would be alternate way to achieve the results I'm looking for but I would like to make this particular solution work.
Since the TextField doesn't want to accept our paste event we will find someone else who will. Here is the mx:TextArea workaround:
MXML:
<mx:Canvas id="canvas" paste="pasteHandler(event)">
<mx:TextArea id="textArea" textInput="if (event.text.length > 1) event.preventDefault();"
keyDown="keyDown(event)" mouseEnabled="false"/>
</mx:Canvas>
AS:
private function keyDown(event:KeyboardEvent):void{
//When CTRL-V is pressed set focus to canvas
if(event.keyCode==86 && event.ctrlKey)
canvas.setFocus();
}
You also need enable the clipboard items on the Canvas's ContextMenu and reset focus to TextArea when finished with the paste.
Got the paste-blocking solution from: flex: how to prevent PASTE (ctrl+V) in a flex3 textinput?
This problem will often occur when large amounts of antialiased text are added to the display list. Once the text is rendered, everything is fine again. You can go around this problem, if you're handling predefined text, by splitting large portions of text into a lot of small ones, and adding them to the stage piece by piece (say, 20 lines of text at a time), waiting one frame between each, allowing the screen to refresh.
I haven't tried this yet, but I would suggest adding an event listener to the TextArea and checking on Event.RENDER, if the text was changed. If this is true, you could remove all text that was added since the last render event, and frame by frame re-add it much like in the example above.
Also, try using native instead of embedded fonts and switching off antialiasing, or reducing its quality.
Unfortunately, I think that you can't intercept all types of user input event on text fields using Flex 3.
If you can switch to Flex 4 (thus using the new FTE), then you should add a listener on the TextOperationEvent.CHANGING event , that is a cancellable event. In the handler, you could verify the amount of text that is being added and, if it is too much, cancel the event (event.preventDefault()) and add it in multiple frames.
The nice thing about this event is that it fires also for event such as "delete key pressed with selected text", or cut/copy/paste/delete operations. In fact, I use it to apply early validation to some kind of text fields, that I couldn't detect in any way with Flex 3.
EDIT
Maybe I found a solution.. I noticed that the "restrict" property, that allows to filter the characters allowed in a text field, is supported in the built-in TextField class, and it is not a feature added by the wrapping Flex component: interesting for our porpuse.
So I tried to play with it, and I discovered that setting restrict="" on your TextArea will prevent blocking the UI on paste of a large block of text, because it is applied "early" at the player level. The nice thing is that, even with this setting, the TextArea will still dispatch the textInput event, so you can monitor the paste event and decide what to do with the new text: update the text property in a single step, start an incremental update or just ignore the event.
Copy and paste the following code to see an example (tested with Flex 3.6 and Flash Player 11):
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
minWidth="955" minHeight="600"
layout="vertical"
horizontalAlign="center">
<mx:Script>
<![CDATA[
protected function generateClipboard():void {
var largeData:String = "";
while (largeData.length < 1000000) {
largeData += "Lorem ipsum ... ";
}
System.setClipboard(largeData);
}
protected function ontextarea1_textInput(event:TextEvent):void {
if (event.text.length < 100) {
// NAIVE CODE: just to demonstrate that you can
// programmatically modify the text.. you should "merge"
// the new text with existing content here
textArea.text += event.text;
} else {
// do something here, like starting
// to add text block by block in
// multiple frames
}
}
]]>
</mx:Script>
<mx:TextArea id="textArea" width="100%" height="100%" restrict="" textInput="ontextarea1_textInput(event)"/>
<mx:Button click="generateClipboard()" label="Set clipboard"/>
</mx:Application>
The tricky part will be correctly set the text on the textInput event, and the "incremental" load of large text.
Its impossible with such large texts (you mentioned that the text is around 7MB).
If you look at the source of mx:TextArea, it inserts text simply be setting its TextField text property to the string you entered:
From TextArea.as around line 2050:
if (textChanged || htmlTextChanged)
{
// If the 'text' and 'htmlText' properties have both changed,
// the last one set wins.
if (isHTML)
textField.htmlText = explicitHTMLText;
else
textField.text = _text;
textFieldChanged(false, true)
textChanged = false;
htmlTextChanged = false;
}
If you make a sample app with a TextField and try to set its text property with a large string, you will get script timeouts. (i got timeout when i tried it with a string length of 1MB):
import flash.text.TextField;
tf = new TextField();
addChild(tf);
tf.multiline = true;
tf.wordWrap = true;
tf.width= 600
tf.height = 500
var str:String = "";
for (var i:int=0; i<100000; i++) {
str = str+"0123456789"
}
tf.text = str
This is the simplest possible way to display text, and it timeouts. Flex will try to do a dozen of more things with this textfield before laying out...so it will give up at much smaller size texts too.
The only possible solution is to make a custom textfield, and add the text gradually - Adobe recommends to use TextField.appendText():
import flash.text.TextField;
public function test(){
tf = new TextField();
addChild(tf);
tf.multiline = true;
tf.wordWrap = true;
tf.width= 600
tf.height = 500
addEventListener(Event.ENTER_FRAME,onEF);
}
private var cnt:int = 0;
private function onEF(event:Event):void{
if (cnt>200) return;
trace (cnt);
var str:String = "";
//pasting around 50K at once seems ideal.
for (var i:int=0; i<5000; i++) {
str = str+"0123456789"
}
tf.appendText(str);
cnt++;
}
This script manages to add 10MB of text into the TextField... although after adding 1MB it gets increasingly slower (it took around 1 sec for an iteration at the end). And if you try to do anything with this textfield (for example resizing, modifying text not with appendText(), adding it to the stage after its complete...) you will get script timeout.
So the complete solution is to capture the paste event canvas wrapper trick (William's solution), and then add the text gradually to a custom component, that uses appendText to add text. (use flash.text.TextField, and add it to the stage with mx_internal::$addChild() ). I have not tested, whether you can remove the resulting monstrosity with removeChild without triggering a script timeout :).
In the Spark TextArea the Paste Event fires correctly. There might be a cleaner way to do this but this is the solution I found.
MXML:
<s:TextArea id="textArea" changing="changeHandler(event)"
paste="pasteHandler(event)" />
AS:
private var pastedText:String;
private var textPosition:int=0;
//Number of characters to read per frame
private var chunkSize:int=1000;
private function changeHandler(event:TextOperationEvent):void{
if(String(event.operation)=="[object PasteOperation]"){
event.preventDefault();
}
}
private function pasteHandler(event:Event):void{
pastedText = String(Clipboard.generalClipboard.getData(ClipboardFormats.TEXT_FORMAT));
this.addEventListener(Event.ENTER_FRAME,frameHandler);
}
private function frameHandler(event:Event):void{
if( textPosition + chunkSize > pastedText.length ){
chunkSize = pastedText.length - textPosition;
this.removeEventListener(Event.ENTER_FRAME,frameHandler);
}
textArea.text += pastedText.substr(textPosition,chunkSize);
textPosition += chunkSize;
}

Handling error conditions on Flex

I have the following AS code.I have noticed that if an Application i s using the webcamera then it cannot be used by any secondary applications until unless the primary application is closed.
My question is that from the following code 1.can we capture that condition
2.If no camera is detected how to give the alert since it is an AS code
EDIT:
Filename is cldAS.as
Now how to call cldAS() from any.mxml file .Some example would be appreciated
package org.com
{
import flash.display.Sprite;
import flash.media.*;
import flash.net.*;
public class cldAS extends Sprite
{
public function cldAS()
{
var cam:Camera = Camera.getCamera();
if(cam != null)
{
cam.setMode(640, 480, 30);
var video:Video = new Video(300, 450);
video.attachCamera(cam);
addChild(video);
}
else
{
trace("No Camera Detected");
//How to give an alert here
}
}
}
}
Alert.show("You don't seem to have a webcam.");
instead of
trace(...) ?
Alert is available in Flex only , in AS3 you should really implement your own solution, on the other hand , since Alert is a Javascript function , you could also use ExternalInterface to call it.
As far as implementing your own solution is concerned, at the minimum you need a TextField to display your message, which text you could provide by sending a CustomEvent with a message property that will simply take a String. It wouldn't take too much work to create your own Alert class.It would sit on top of your App , you could toggle visibility when receiving a CustomEvent and have a Close button to hide it.
You should be able to call your AS3 class within script tags , other than that I'll leave a more detailed answer to Flex experts. I'm not sure if you can add a Sprite directly into Flex , for all I remember an object in Flex must inherit from UIComponent in order to be added to the stage but check with the other guys here, I haven't used Flex in quite some time...
<mx:Script>
import org.com.cldAS;
public cld:cldAS = new cldAS();
</mx:Script>

Flex: Popup Window - Get [ok] or [cancel]

I've done a lot of C# programming with both Winforms and WPF. I'm working on a Flex/Air app now for cross platform support. But this is my first flex project, so I'm learning as I go.
I've got a window that I want to popup, that the user will fill out a form, then hit OK or CANCEL. I set it up the same way I would've in C#, but it doesn't work, and I can't really see a way to make it do what I want.
EDIT:
So I'm trying events now, the events just don't seem to be handled...
EDIT again:
Oh, It's because the popup manager seems to create a new instance of the Form object, rather than using the one I created already.
so in the showWindow method, I put in this code rather than the popup manager:
parent.addChild(this);
then I remove it when I close it. The only problem is, it doesn't disable the rest of the parent like the popup manager does. Any suggestions on that?
PARENT:
private function btnAdd_Clicked():void
{
var form:Form = new Form();
form.addEventListener(CloseEvent.CLOSE, onFormClosed, false, 0, true);
recipeForm.showWindow(this);
}
private function onFormClosed(e:CloseEvent):void
{
//none of these Alerts are ever shown. I also tried breakpoints in debug to try an follow the code, with no luck
Alert.show("Closed");
if(e.detail == Alert.OK)
{
Alert.show("OK");
}
else if(e.detail == Alert.CANCEL)
{
Alert.show("Cancel");
}
}
CHILD:
private function btnCancel_Clicked():void
{
okClicked = false;
closeWindow();
}
public function closeWindow():void
{
var e:CloseEvent = new CloseEvent(CloseEvent.CLOSE);
e.detail = okClicked ? Alert.OK : Alert.CANCEL;
dispatchEvent(e);
PopUpManager.removePopUp(this);
}
public function showWindow(parent:WindowedApplication):void
{
var window:IFlexDisplayObject = PopUpManager.createPopUp(parent, RecipeForm, true);
PopUpManager.centerPopUp(window);
}
You can do this at least two different ways:
FIRST WAY: Using events
Let your Form class dispatch an event when either of the buttons is clicked. After Form is instantiated from the parent view, add an eventListener for the event(s) it's known to dispatch. When the Form dispatches the event, the eventListener will be invoked. You can even reuse Flex's CloseEvent and set the "detail" property to either Alert.OK or Alert.CANCEL before dispatching it.
In Form:
var e:CloseEvent = new CloseEvent(CloseEvent.CLOSE);
e.detail = okClicked ? Alert.OK : Alert.CANCEL;
dispatchEvent(e);
In parent:
var f:Form = new Form();
f.addEventListener(CloseEvent.CLOSE, onClose, false, 0, true);
...
private function onClose(e:CloseEvent):void
{
if (e.detail == Alert.OK)
// do something
else if (e.detail == Alert.CANCEL)
// do something else
}
SECOND WAY: Using callbacks
Add a public var of type "Function" to your Form class and supply a callback function from the parent. This does basically the same thing as #1 except with little less abstraction / indirection.
I would recommend #1 since the event model in Flex is pretty well-conceived and more flexible than the callback.
In Form:
var e:CloseEvent = new CloseEvent(CloseEvent.CLOSE);
e.detail = okClicked ? Alert.OK : Alert.CANCEL;
dispatchEvent(e);
In parent:
var f:Form = new Form();
f.addEventListener(CloseEvent.CLOSE, onClose, false, 0, true);
...
private function onClose(e:CloseEvent):void
{
if (e.detail == Alert.OK)
// do something
else if (e.detail == Alert.CANCEL)
// do something else
}
Not sure if this is still an open issue. I ran into this very same problem and I think I figured out what is wrong. At least I did for my problem.
I implemented things exactly as you did. I also have the close attribute set to closeWindow (I'm using a TitleWindow for my dialog).
So when the window is closed via the X at the top, it will call closeWindow, also if you click on the Cancel button, it will also call closeWindow.
The problem for me was that clicking cancel, dispatches a CloseEvent which seems to be caught by a Listener which calls closeWindow again (possibly via the close attribute which probably creates its own internal listener). I'm not sure if its an infinite loop but Flex does not like this.
My solution was to create two functions, one for the X close window to call and one for the Cancel button to dispatch a CloseEvent of its own. This seemed to work for me. Hope it helps you.

Resources