HTTPService/ResultEvent with Flex 3.2 versus Flex >= 3.5 - actionscript-3

through a design decission or what-so-ever Adobe changed the content of the ResultEvent fired by a HTTPService Object.
Take a look at following example:
var httpService:HTTPService = myHTTPServices.getResults();
httpService.addEventListener(ResultEvent.RESULT,resultHandler);
httpService.send();
/**
* Handels the login process
*/
function resultHandler(event:ResultEvent):void
{
// get http service
var httpService = (event.target as HTTPService);
// do something
}
It works like a charm with Flex 3.2. But when I try to compile it with Flex 3.5 or Flex 4.0 event.target as HTTPService is null.
I figured out that event.target is now an instance of HTTPOperation. That is interesting because I can't find HTTPOperation in the langref. However, I think what Flash Builder's debugger means is mx.rpc.http.Operation.
The debugger also shows that event.target has a private attribute httpService which is the instance I expected to get with event.target. But it's private, so event.target.httpService doesn't work.
If I only want to remove the EventListener I can cast event.target as EventDispatcher. But I need to use methods from HTTPService.
So: How can I get the HTTPService instance from the ResultEvent?
Any help would be appreciated. Thanks!
J.

It is useful to go through the source if you get into this. On OS X the rpc classes are here: /Applications/Adobe Flash Builder Beta 2/sdks/3.4.1/frameworks/projects/rpc/src
Inside mx.rpc.http.HTTPService there is indeed an inner-class named HTTPOperation.
It extends mx.rpc.http.AbstractOperation which in turn extends mx.rpc.AbstractOperation. Inside AbstractOperation is a getter method get service which looks to return what you need.
Since HTTPService is an inner-class it is effectively private so you'll need to cast to an AbstractOperation (either mx.rpc.http.AbstractOperation or mx.rpc.AbstractOperation).
So something like:
function resultHandler(event:ResultEvent):void
{
// get the operation
var operation:AbstractOperation = AbstractOperation(event.target);
// get http service
var httpService:HTTPService = HTTPService(operation.service);
}
edit: I take it back! Looks like Adobe is sending null for the service when it calls the super when constructing the HTTPOperation. The HTTPService is therefore only cached in the private variable httpService. I have no idea why they hide it from you but it looks like you'll have to keep your own reference around.

I solved this problem for myself.
There are some properties in HTTPService that are available from AbstractOperation. For example, I use property request which is an Object:
myService.request["service"] = myService;
And later, when I get Event which has HTTPOperation in event.currentTarget, I get my HTTPService in such way:
var eventService : HTTPService = HTTPService( AbstractOperation( event.currentTarget ).request["service"] );

Related

Actionscript 3 - can't open multiple navigateToURL() instances at the same time

I am new to AS3, I want to open multiple browser tabs with flash.
I'm trying to simply start multiple instances of navigateToURL().
for each (var str:String in arrayofrequests)
{
[...]
try { navigateToURL(request, "_blank");}
[...]
}
but only the last instance of navigateToURL gets executed in the browser.
I searched online and someone pointed out callLater could solve this issue.
But every time I try to use callLater I get
Error: Call to a possibly undefined method callLater.
I analyzed adobe documentation here: http://help.adobe.com/en_US/flex/using/WS2db454920e96a9e51e63e3d11c0bf69084-7b06.html
All objects that inherit from the UIComponent class can open the callLater() method.
How I do this? I tried to change my code to something like this
public class Main extends UIComponent
but it isn't working.
To start, UIComponent class is the base class for all visual components used in Flex ( like Label, Progressbar, ...), but I think that your are using Flash, so it's not the good way.
Really I don't know why you want to open many urls in the browser in the same time ( and I think that your final user may be will not like that ), but you have to use some intervals between every navigateToURL() calls using a Timer object for example :
var urls:Array = [
'http://www.wikipedia.org',
'http://www.ubuntu.com',
'http://www.stackoverflow.com'
];
var timer:Timer = new Timer(300, urls.length);
timer.addEventListener(TimerEvent.TIMER, onTimer);
function onTimer(e:TimerEvent):void {
navigateToURL(new URLRequest(urls[timer.currentCount - 1]), '_blank');
}
timer.start();
Hope that can help.

mvvmcross: NavigationService.Navigate throws an MvxException "Unable to find incoming mvxviewmodelrequest"

In my WP8 app, I have MainView referencing MainViewModel. MainView is a menu where users can navigate to other views to do some tasks. Navigating from MainView works perfectly as I use ShowViewModel. However, navigating from other views when user completes a task, back to MainView using NavigationService.Navigate(URI) throws an exception "Unable to find incoming mvxviewmodelrequest".
To avoid this exception, I have construct the URI like below
var req = "{\"ViewModelType\":\"MyApp.Core.ViewModels.MainViewModel, MyApp.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null\",\"ClearTop\":\"true\",\"ParameterValues\":null,\"RequestedBy\":null}";
NavigationService.Navigate(new Uri("/MainView.xaml?ApplicationUrl=" + Uri.EscapeDataString(req), UriKind.Relative));
Does anyone have a better way to use NavigationService.Navigate?
Most navigations in the MvvmCross samples are initiated by either MvxAppStart objects or by MvxViewModels. Both of these classes inherit from MvxNavigatingObject and use the ShowViewModel methods exposed there - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross/ViewModels/MvxNavigatingObject.cs
From MvxNavigatingObject, you can see that MvvmCross routes the navigation call to the IMvxViewDispatcher which in WindowsPhone is a very thin object - all it does is marshall all calls to the UI thread and to pass them on to the IMvxViewPresenter - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/MvxPhoneViewDispatcher.cs
The presenter is an object created in Setup - and the default implementation uses an IMvxPhoneViewModelRequestTranslator to convert the navigation call into a uri-based navigation - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/MvxPhoneViewPresenter.cs
Silverlight/WindowsPhone then uses this uri for navigation, creates the necessary Xaml page, and then calls OnNavigatedTo on this page. As part of the base.OnNavigatedTo(); handing in MvxPhonePage, MvvmCross then calls the OnViewCreated extension method. This method checks if there is already a ViewModel - if there isn't one then it attempts to locate one using the information in the uri - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/MvxPhoneExtensionMethods.cs
With this explanation in mind, if any app ever wants to initiate an MvvmCross navigation from a class which doesn't already inherit from MvxNavigatingObject - e.g. from some Service or from some other class, then there are several options:
You can provide a shim object to do the navigation - e.g.:
public class MyNavigator : MvxNavigatingObject {
public void DoIt() {
ShowViewModel<MyViewModel>();
}
}
// used as:
var m = new MyNavigator();
m.DoIt();
You can instead use IoC to locate the IMvxViewDispatcher or IMvxViewPresenter and can call their Show methods directly
var request = MvxViewModelRequest<MyViewModel>.GetDefaultRequest();
var presenter = Mvx.Resolve<IMvxViewPresenter>();
presenter.Show(request);
You can write manual code which mimics what the IMvxViewPresenter does - exactly as you have in your code - although it might be "safer" to use the IMvxPhoneViewModelRequestTranslator.cs to assist with generate the url - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/IMvxPhoneViewModelRequestTranslator.cs
var request = MvxViewModelRequest<MyViewModel>.GetDefaultRequest();
var translator = Mvx.Resolve<IMvxPhoneViewModelRequestTranslator>();
var uri = translator.GetXamlUriFor(request);
One other option that Views always have is that they don't have to use the standard MvvmCross navigation and ViewModel location. In WindowsPhone, your code can easily set the ViewModel directly using your own logic like:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (ViewModel == null) {
ViewModel = // something I locate
}
// if you are doing your own logic then `base.OnNavigatedTo` isn't really needed in winphone
// but I always call it anyway
base.OnNavigatedTo(e);
}
Alternatively in WindowsPhone, you can even replace MvxPhonePage with a different base class that uses it's own logic for viewmodel location. This is easy to do in WindowsPhone as all Xaml pages have built-in data-binding support.

How to do CreateBindingSet() on Windows Phone?

In the N+1 video #34 (Progress), there was an example of using CreateBindingSet() for the Android version, which is not typical. But the narrator also mentioned briefly that the same can be done on the Windows platform.
As much as I tried, however, I am unable to get a View's property to be bound to its ModelView on the Windows Phone. I always get a NullReferenceException.
The closest I came was the code below, including suggestions from ReSharper. Here's my FirstView.xaml.cs:
using Cirrious.MvvmCross.Binding.BindingContext;
using Whatever.ViewModels;
namespace Whatever {
// inheriting from IMvxBindingContextOwner was suggested by ReSharper also
public partial class FirstView : BaseView, IMvxBindingContextOwner {
public class MyBindableMediaElement
{
private string _theMediaSource = "whatever";
public string TheMediaSource
{
get
{
return _theMediaSource;
}
set
{
_theMediaSource = value;
}
}
}
public FirstView()
{
InitializeComponent();
_mediaElement = new MyBindableMediaElement(this.theMediaElement);
var set = this.CreateBindingSet<FirstView, FirstViewModel>();
// the corresponding view model has a .SongToPlay property with get/set defined
set.Bind(_mediaElement).For(v => v.TheMediaSource).To(vm => vm.SongToPlay);
set.Apply();
}
public IMvxBindingContext BindingContext { get; set; } // this was suggested by ReSharper
}
I get a NullReferenceException in MvxBaseFluentBindingDescription.cs as soon as the view is created. The exact location is below:
protected static string TargetPropertyName(Expression<Func<TTarget, object>> targetPropertyPath)
{
var parser = MvxBindingSingletonCache.Instance.PropertyExpressionParser; // <----- exception here**
var targetPropertyName = parser.Parse(targetPropertyPath).Print();
return targetPropertyName;
}
I have not seen a working example of creating a binding set on a Windows Phone emulator. Has anyone gotten this to work? Thanks.
I can confirm that the narrator said that remark a little too flippantly without actually thinking about how he might do it...
However, with a little effort, you definitely can get the CreateBindingSet to work in Windows if you want to.
Before you start, do consider some alternatives - in particular, I suspect most people will use either Windows DependencyProperty binding or some hand-crafted code-behind with a PropertyChanged event subscription.
If you do want to add CreateBindingSet code to a Windows project then:
Add the Binding and BindingEx assemblies to your Ui project - the easiest way to do this is using nuget to add the BindingEx package.
In your Setup class, override InitializeLastChance and use this opportunity to create a MvxWindowsBindingBuilder instance and to call DoRegistration on that builder. Both these first two steps are covered in the n=35 Tibet binding video - and it's this second step that will initialise the binding framework and help you get past your current 'NullReferenceException' (for the code, see BindMe.Store/Setup.cs)
In your view, you'll need to implement the IMvxBindingContextOwner interface and you'll need to ensure the binding context gets created. You should be able to do this as simply as BindingContext = new MvxBindingContext();
In your view, you'll need to make sure the binding context is given the same DataContext (view model) as the windows DataContext. For a Phone Page, the easiest way to do this is probably just to add BindingContext.DataContext = this.ViewModel; to the end of your phone page's OnNavigatedTo method. Both steps 3 and 4 could go in your BaseView if you intend to use Mvx Binding in other classes too.
With this done, you should be able to use the CreateBindingSet code - although do make sure that all binding is done after the new MvxBindingContext() has been created.
I've not got a windows machine with me right now so I'm afraid this answer code comes untested - please do post again if it does or doesn't work.
I can confirm it works almost perfectly; the only problem is, there are no defaults register, so one has to do the full binding like:
set.Bind(PageText).For(c => c.Text).To(vm => vm.Contents.PageText).OneTime();
to fix this, instead of registering MvxWindowsBindingBuilder, I am registering the following class. Note: I have just created this class, and needs testing.
public class UpdatedMvxWindowsBindingBuilder : MvxWindowsBindingBuilder
{
protected override void FillDefaultBindingNames(IMvxBindingNameRegistry registry)
{
base.FillDefaultBindingNames(registry);
registry.AddOrOverwrite(typeof(Button), "Command");
registry.AddOrOverwrite(typeof(HyperlinkButton), "Command");
//registry.AddOrOverwrite(typeof(UIBarButtonItem), "Clicked");
//registry.AddOrOverwrite(typeof(UISearchBar), "Text");
//registry.AddOrOverwrite(typeof(UITextField), "Text");
registry.AddOrOverwrite(typeof(TextBlock), "Text");
//registry.AddOrOverwrite(typeof(UILabel), "Text");
//registry.AddOrOverwrite(typeof(MvxCollectionViewSource), "ItemsSource");
//registry.AddOrOverwrite(typeof(MvxTableViewSource), "ItemsSource");
//registry.AddOrOverwrite(typeof(MvxImageView), "ImageUrl");
//registry.AddOrOverwrite(typeof(UIImageView), "Image");
//registry.AddOrOverwrite(typeof(UIDatePicker), "Date");
//registry.AddOrOverwrite(typeof(UISlider), "Value");
//registry.AddOrOverwrite(typeof(UISwitch), "On");
//registry.AddOrOverwrite(typeof(UIProgressView), "Progress");
//registry.AddOrOverwrite(typeof(IMvxImageHelper<UIImage>), "ImageUrl");
//registry.AddOrOverwrite(typeof(MvxImageViewLoader), "ImageUrl");
//if (_fillBindingNamesAction != null)
// _fillBindingNamesAction(registry);
}
}
This is a skeleton from Touch binding, and so far I have only updated three controls to test out (Button, HyperButton and TextBlock)

As3 imported custom class won't access components at main stage

I have this situation. I was building all in code, but it's a little painful, so I made a interface with components using the Flash drawing capabilities.
I got a main class, as usual, with the interface in a MovieClip instance called "AreaEdit". In my custom class "EditorHTML" there is a Sprite:
private var dTela:Sprite;
So the constructor is like this:
public function EditorHTML(instEdit) {
this.Parags = new Array();
this.dTela = instEdit;
trace("dTela: "+this.dTela.width+" x "+this.dTela.height);
}
At the main class:
Escrit = new EditorHTML(AreaEdit);
So trace displays the dimensions of the box, as expected. However, at the custom class, if I try to access an instance inside like this:
this.dTela.cxEdit.addEventListener(Event.CHANGE, atualizar);
An error is returned: /Library/WebServer/Documents/as3/bibliotecas_externas/com/gustavopi/txt/EditorHTML.as, Line 49 1119: Access of possibly undefined property cxEdit through a reference with static type flash.display:Sprite.
I did a test and the same instance "cxEdit" is available in main class. So it seams the components instances are not available for a custom class. How do I solve this?
Try to call it like this:
Sprite( Sprite(this.dTela).getChildByName("cxEdit")).addEventListener(Event.CHANGE, atualizar);
In case that cxEdit is a Sprite too.
Edited: cxEdit must be a TextArea. So it can be done like this:
var cxEdit:TextArea = TextArea(Sprite(this.dTela).getChildByName("cxEdit"));
cxEdit.addEventListener(Event.CHANGE, atualizar);
To make it easier for the rest of the code...
From what I can see, you are trying to access the "cxEdit" as a property of Sprite (dTela), which is not a Sprite property, hence the error.
Could you perhaps pass in AreaEdit.cxEdit as the argument instead of just AreaEdit?

Selecting Input TextField throws Security sandbox violation in loaded swf in Adobe AIR

I have a swf, loaded into the non-application sandbox in Adobe AIR 1.5 (the shell is already installed with our users so I can't update to version 2+).
On the stage in the swf are buttons, movieclips, animations etc - all of these work fine.
When we add an input TextField, selecting this TextField causes a Security Sandbox Violation.
Error message (in debug mode) is (I've edited the actual file names):
[trace] *** Security Sandbox Violation ***
[trace] SecurityDomain 'file:///path/to/local/loaded.swf' tried to access incompatible context 'app:/loadingApp-debug.swf'
The user then is unable to enter text into the TextField. The rest of the application is unaffected.
The FDB stacktrace only shows:
this = [Object 57216577, class='flash.utils::Timer'].Timer/tick() at <null>:0
Has anyone got a workaround for this?
I'm guessing it's either the TextField attempting to access the stage, or an event attempting to bubble / access global properties.
I understand the air sandbox restrictions and use them daily - with sandboxBridges from parent to child and child to parent etc - perhaps there is something I need to expose to allow this to work?
Any clues?
Edit:
I've now tracked down the problem to being that the TextField attempts to do
this.stage.focus = this;
or something equivalent when MouseDown happens.
It also appears that there is no access to KeyboardEvents in loaded swfs, so my thought of making the 'field' a button and then controlling input by listening to KeyboardEvents is dead in the water.
Now looking at whether to relay events to callbacks passed through the parent sandbox bridge, or whether minimal comps might save my butt.
Ok, I have an insane workaround, but it's pretty solid. I'm going to post it almost in full here, though I'll probably make it generic and upload it to github at some point.
In my shell, I have a view-with-mediator (I'm using robotlegs) which I'm calling EventRelayer and EventRelayerMediator.
The view's only purpose is to give the mediator access to the stage.
I exposed some functions on the parentSandboxBridge:
public function requestKeyboardEventRelay(eventType:String, callback:Function):void;
public function requestMouseEventRelay(eventType:String, callback:Function):void;
public function cancelKeyboardEventRelay(eventType:String, callback:Function):void;
public function cancelMouseEventRelay(eventType:String, callback:Function):void;
My sandbox bridges always just translate into strong typed events, so these fire events like:
RelayEvent(RelayEvent.START_RELAY_REQUESTED, KeyboardEvent, eventType, callback);
RelayEvent(RelayEvent.CANCEL_RELAY_REQUESTED, MouseEvent, eventType, callback);
These are picked up by the EventRelayerMediator, and translated into handlers in an eventMap:
override public function onRegister():void
{
createRelayHandlerFactories();
eventMap.mapListener(eventDispatcher, RelayEvent.START_RELAY_REQUESTED, startRelay);
}
protected function startRelay(e:RelayEvent):void
{
var handler:Function = createRelayHandler(e.relayEventClass, e.callback);
eventMap.mapListener(view.stage, e.relayEventType, handler, e.relayEventClass);
}
protected function createRelayHandler(relayEventClass:Class, callback:Function):Function
{
var handler:Function = relayHandlerFactoriesByEventClass[relayEventClass](callback);
return handler;
}
protected function createRelayHandlerFactories():void
{
relayHandlerFactoriesByEventClass = new Dictionary();
relayHandlerFactoriesByEventClass[KeyboardEvent] = createKeyboardEventRelayHandler;
relayHandlerFactoriesByEventClass[MouseEvent] = createMouseEventRelayHandler;
}
protected function createKeyboardEventRelayHandler(callback:Function):Function
{
var handler:Function = function(e:KeyboardEvent):void
{
trace("Relaying from shell: " + e.toString());
// passing an object because the sandbox bridge doesn't allow strong typed values, only primitives
var o:Object = {};
o.type = e.type;
o.charCode = e.charCode;
o.keyCode = e.keyCode;
o.altKey = e.altKey;
o.ctrlKey = e.ctrlKey;
o.shiftKey = e.shiftKey;
// no point adding other props as we can't pass them
// to the constructor of the KeyboardEvent
callback(o)
}
return handler;
}
The loaded swf passes a callback which just re-assembles and re-dispatches the events.
My input TextField is now just a dynamic field with a click handler that activates listening for keyboard events on the root of the swf, and then updates the dynamic field accordingly.
At the moment that is super-crude but I'll break it out into a robust, tested class now I know it works.
I've used a dictionary to manage the handlers because I'm sure that memory leakage hell is about to follow and I'm expecting to have to relay the FocusEvents to stop entering text.
I need to test memory leakage, return a binding from the parentSandboxBridge function so that I can make sure I don't add the same handler twice etc etc, but Adobe - you suck for not calling this out and providing a built in relay mechanism.