2
votes

I have a panorama viewer in Flash in which I want to embed a small Flex application. I've managed to load the Flex swf into the Flash like this:

private var loader:Loader = new Loader();

var req:URLRequest = new URLRequest("SimpleImageViewer.swf");
loader.load(req);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imgViewerLoaded);

imgViewerLoaded(event:Event)
{
    imageViewer = loader.content;
    addChild(imageViewer);
    imageViewer.showImage("test.jpg", "Test Image");
}

Now, the thing is that I need to be able to call a method on the imageViewer, which I've defined in my Flex App like this:

public function showImage(source:String, heading:String = ""):void
{
    //Show the image
}

It is also being added to the ExternalInterface using

ExternalInterface.addCallback("showImage", showImage);

How can I call that method from my AS3 Flash? Just calling it as imageViewer.showImage(...) gives this error:

ReferenceError: Error #1069: Property showImage not found on _SimpleImageViewer_mx_managers_SystemManager and there is no default value.

at UserInterface/imgViewerLoaded()

This is the entire SimpleImageViewer.mxml file:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" minWidth="100" minHeight="100" backgroundColor="#2B2B2B" creationComplete="onCreationComplete(event)" backgroundGradientAlphas="[1.0, 1.0]" backgroundGradientColors="[#2B2B2B, #2B2B2B]" verticalScrollPolicy="off" horizontalScrollPolicy="off">
<mx:Image x="0" y="0" width="100%" height="100%" horizontalAlign="center" verticalAlign="middle" scaleContent="true" maintainAspectRatio="true" id="picture"/>
<mx:Image width="100%" height="42" x="0" y="0" id="headingBg" source="@Embed(source='/bilder/topList.png')" scaleContent="true" maintainAspectRatio="false"/>
<mx:Label x="4" y="0" width="100%" height="43" id="headingTxt" fontFamily="Arial" fontSize="32" color="#FFFFFF" textAlign="center"/>

<mx:Script>
    <![CDATA[
        import mx.events.FlexEvent;
        import mx.events.ResizeEvent;
        
        public function showImage(source:String, heading:String = ""):void
        {
            picture.source = source;
            picture.load();
            headingTxt.text = heading;
        }
        
        protected function onCreationComplete(event:FlexEvent):void
        {               
            ExternalInterface.addCallback("showImage", showImage);
            Security.allowDomain("localhost");
        }


    ]]>
</mx:Script>
</mx:Application>

And the ImageViewer interface:

package tikab.lizzan
{
    public interface ImageViewer
    {
        function showImage(source:String, heading:String = ""):void ;   
    }
}
3
Your error indicates a problem with typing. Are you implementing an interface? You need to cast imageViewer as its interface type, so flash knows there is a method called "showImage". ... One other thing - the error is at imgViewerLoaded() - but you don't appear to be calling it there. Altered code? - Bosworth99
@Bosworth99: Added the missing call. I've tried using an interface, as per @weltraumpirat's answer, but that just gives me another error message (see my comment there). - Lizzan

3 Answers

2
votes

Edited:

From the flex documentation:

If an application is loaded into another application, a SystemManager will still be created, but will not manage an application window, depending on security and domain rules. Instead, it will be the content of the Loader that loaded it and simply serve as the parent of the sub-application

In other words: When you load your flex application into another application, the content property of the loader object will not point directly to the application itself, but to a SystemManagerinstance, which acts as the parent of the actual Application object. Your method calls must therefore be directed at loader.content.getChildAt(0).

To do this in a type-safe way:

  1. create an Interface
  2. have your flex document class implement it
  3. cast the first Child of your Loader object's content to that same interface

Or (not type-safe, but quick) simply call the method using bracket syntax:

loader.content.getChildAt(0)["showImage"]();

or cast loader.content.getChildAt(0) to a dynamic type, like Object or MovieClip, as @_asMan recommended.

Edit:

You are also missing some important declaration statements in your code. This is what the imageViewerLoaded function should look like:

function imgViewerLoaded(event:Event)
{
    var imgViewer:MovieClip = loader.content.getChildAt(0) as MovieClip;
    addChild(loader.content);
    imgViewer.showImage("test.jpg", "Test Image");
}

function and var should never be considered optional - they help you to prevent messing up your class hierarchy and type references. (You can omit var if you have imgViewer declared as a member variable somewhere else, of course) I also changed the name from "imageViewer" to ensure there is no ambiguous reference - the error statement you posted below implies a risk ;)

0
votes

Not sure this will work but, have you tried to case it as an object?

imgViewerLoaded(event:Event)
{
    imageViewer = loader.content;
    addChild(imageViewer);
    Object( loader.content ).showImage();
}

or perhaps casing as the base class if you have one assigned. I used Application as an example

var loadedApp:Application = Application( loader.content );
loadedApp.showImage();
0
votes

EDIT #2:

Try this:

function imgViewerLoaded(event:Event):void
{
    var manager:SystemManager = event.target.content as SystemManager;
    var content:ImageViewer = manager.application as ImageViewer;
    addChild(viewer);
    viewer.showImage();
}

This builds on weltraumpirat's helpful insight that the content type is actually a SystemManager object. Based on everything I see, I think this will work and be a little cleaner. Good luck!

** of course, note that you would have to modify your posted MXML to implement your interface, as I suspect you've already done

EDIT #1:


Based on your messages above, I found some of my code that does the same kind of thing:

private function loadCompleted(event:Event):void {
        currentClip = event.target.content as MovieClip;
        loader.unload();
        displayClip();
}

So you might want to also try looking at the contents of:

event.target.content

Try the following code to see what the type is, then use that information for casting:

var contents:* = event.target.content;
var className:String = flash.utils.getQualifiedClassName( contents );
trace("Class name was: " + className);

From there you might be able to access the method a little more cleanly with code along the lines of the following:

function imgViewerLoaded(event:Event):void
{
    var viewer:TypeThatYouFound = loader.content as TypeThatYouFound;
    addChild(viewer);
    viewer.showImage();
}

Of course, it might just turn out that the event contents are the exact same as the Loader contents. In which case, this approach wouldn't be significantly better. But, it's worth a shot.

Hope that helps in some way,

~gMale


Ok. So I won't even ask why you're doing this in reverse (typically, you go from flash to flex not the other way around. It's common to import swfs into Flex not Flex into SWFs).

The first thing that comes to mind is that you probably are not creating a class for your Flex code. To expose your functions, you're going to have to let somebody know that your object is different than a typical SimpleImageViewer.

Depending on how you designed things, you might need to make a custom class like

public class MySimpleImageViewer extends SimpleImageViewer {
        public function showImage(source:String, heading:String = ""):void {
            //Show the image
        }
}

Then have your MXML file instantiate this with the following syntax:

<local:MySimpleImageViewer />

With your approach being a little unorthodox, it's hard to say, exactly what you need. If this doesn't help, try posting your Flex code and we can go from there...

Hope that helps,

~gMale