Advanced Data Grid: Error: Bookmark no longer valid - apache-flex

I am working on an indent and outdent for the advanced datagrid. I have a set of functions which work when operating on the underlying data fine, but which throw "Error: Bookmark no longer valid" when operating on the selected items of the datagrid.
When I run this code it runs fine:
indentLeaf(l5)
outdentLeaf(l4)
But this code fails:
adg.selectedItem = l5
indentLeaf(adg.selectedItem as Leaf)
adg.selectedItem = l4
outdentLeaf(adg.selectedItem as Leaf)
The code does not fail in all instances, only for some configurations of the data grid data tree.
The code needs to be run in the debugger version of the flash player if you want to see the error thrown. I have cut and pasted the error I get into the text area for reference as well as below.
The code in the toy app seems to recover ok when the exception is thrown, but in my larger app it leads to hard crashes.
Example code can be found here with view source turned on: http://www.crcarlson.com/adg/ADGArrayCollectionUpdate.swf
To create the error, reset the tree and then click "indent/outdent2"
I would appreciate any suggestions on how to get around this.
The full stack trace looks like this:
Error: Bookmark no longer valid.
at ListCollectionViewCursor/seek()[E:\dev\4.x\frameworks\projects\framework\src\mx\collections\ListCollectionView.as:2417]
at mx.collections::HierarchicalCollectionViewCursor/get current()[E:\dev\4.x\frameworks\projects\datavisualization\src\mx\collections\HierarchicalCollectionViewCursor.as:220]
at mx.collections::HierarchicalCollectionViewCursor/collectionChangeHandler()[E:\dev\4.x\frameworks\projects\datavisualization\src\mx\collections\HierarchicalCollectionViewCursor.as:1143]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.collections::HierarchicalCollectionView/nestedCollectionChangeHandler()[E:\dev\4.x\frameworks\projects\datavisualization\src\mx\collections\HierarchicalCollectionView.as:1595]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.collections::ListCollectionView/dispatchEvent()[E:\dev\4.x\frameworks\projects\framework\src\mx\collections\ListCollectionView.as:1024]
at mx.collections::ListCollectionView/handlePropertyChangeEvents()[E:\dev\4.x\frameworks\projects\framework\src\mx\collections\ListCollectionView.as:1433]
at mx.collections::ListCollectionView/listChangeHandler()[E:\dev\4.x\frameworks\projects\framework\src\mx\collections\ListCollectionView.as:1300]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.collections::ArrayList/internalDispatchEvent()[E:\dev\4.x\frameworks\projects\framework\src\mx\collections\ArrayList.as:673]
at mx.collections::ArrayList/itemUpdateHandler()[E:\dev\4.x\frameworks\projects\framework\src\mx\collections\ArrayList.as:704]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at Leaf/dispatchChildrenChanged()[C:\adobeFlexTraining\_workspace\ADGArrayCollectionUpdate\src\Leaf.as:119]
at Leaf/addChildAt()[C:\adobeFlexTraining\_workspace\ADGArrayCollectionUpdate\src\Leaf.as:63]
at Leaf/move()[C:\adobeFlexTraining\_workspace\ADGArrayCollectionUpdate\src\Leaf.as:96]
at ADGArrayCollectionUpdate/outdentLeaf()[C:\adobeFlexTraining\_workspace\ADGArrayCollectionUpdate\src\ADGArrayCollectionUpdate.mxml:86]
at ADGArrayCollectionUpdate/IO2_clickHandler()[C:\adobeFlexTraining\_workspace\ADGArrayCollectionUpdate\src\ADGArrayCollectionUpdate.mxml:113]
at ADGArrayCollectionUpdate/__IO2_click()[C:\adobeFlexTraining\_workspace\ADGArrayCollectionUpdate\src\ADGArrayCollectionUpdate.mxml:183]

I just found a workaround for this bug (I am using SDK 3.5 but I guess a 4.1 fix would be very much the same). The problem lies within the "current()" getter of the HierarchicalCollectionViewCursor class.
It doesn't catch the CursorError that's caused by an invalid bookmark.
Step 1 is to create a better cursor class:
public class HierarchicalCollectionViewCursor2 extends HierarchicalCollectionViewCursor
{
public function HierarchicalCollectionViewCursor2(collection:HierarchicalCollectionView, model:ICollectionView, hierarchicalData:IHierarchicalData)
{
super(collection, model, hierarchicalData);
}
override public function get current() : Object
{
// original HierarchicalCollectionViewCursor class fails to catch the "bookmark no
// longer valid" Error, which is thrown as a CollectionViewError instance in ListCollectionView,
// but transformed to a CursorError within the same class
try {
var result:Object = super.current;
}
catch (e:CursorError) {
result = null;
}
// done
return result;
}
}
Step 2 is to create a HierarchicalCollectionView class, which returns that new cursor:
use namespace mx_internal;
public class HierarchicalCollectionView2 extends HierarchicalCollectionView
{
public function HierarchicalCollectionView2(hierarchicalData:IHierarchicalData=null, argOpenNodes:Object=null)
{
super(hierarchicalData, argOpenNodes);
}
override public function createCursor() : IViewCursor
{
return new HierarchicalCollectionViewCursor2(this, treeData, this.source);
}
}
Step 3 is to actually use that new HierarchicalCollectionView2 class as your data-provider.
var itemsAC:ArrayCollection = new ArrayCollection();
// add items etc
this.adgDataProvider = new HierarchicalCollectionView2(new HierarchicalData(itemsAC));
Now you would think that all is well BUT the drama wouldn't be complete without another annoying Flex-SDK bug. In this case its:
https://bugs.adobe.com/jira/browse/FLEXDMV-1846
So, Step 4 is to subclass the AdvancedDataGrid component as described in the bug issue.
That's it -- works for me!

This Exeption may hapen in Flex AdvancedDatagrid with HierarchicalData.
When items are added to Dataprovider it notifies the datagrid . the datagrid receives colection change events each time items are added to it.then some Datagrid internal can be messed up .
You could disable the automatic ArayCollecion refresh :
dataprovider.disableAutoUpdate();
and when you datagrid is ready to use refresh datagrid rendering :
if (advancedDataGrid) {
advancedDataGrid.invalidateList();
advancedDataGrid.invalidateDisplayList();
advancedDataGrid.expandAll();
}
I hope this will help.

Sami was right about the internals (of HierarchicalCollectionViewCursor) getting messed up. This was one of the most long-standing bugs in the Flex framework. It was recently documented - and fixed - at FLEX-34119 and all its linked tickets. It should be available in Flex SDK 4.14, which means that no workaround will be needed from that point onward.

Related

Vaadin 8 Grid EditorSaveListener

In Vaadin 8, given the following Grid definition:
grid = new Grid<>();
grid.getEditor().setEnabled(true);
carBinder = grid.getEditor().getBinder();
with:
grid.getEditor().addSaveListener(event -> {
try {
Binder<Car> binder = event.getSource().getBinder();
grid.getDataProvider().refreshAll();
} catch(Exception e)
ExceptionNotification.show(e);
}
});
The problem I have, I currently cannot access the modified value from the Grid Editor, neither over event.getSource() nor over the binder, defined locally or in class scope.
In the debugger, I see in the Vaadin class com.vaadin.ui.components.grid.EditorImpl a property edited, containing the values, but is private, so not accessible.
The binder.bean value is NULL, in the debug. This would be the value retrieved by: binder.getBean().
So I tried many ways, but currently I could not get the edited value with the save listener, for working with it, and search some help or inspiration for solving the problem.
To get the values use:
binder.writeBean(aCarObject);
According to the documentation it writes the values of the fields into the given object, but throws an exception if any of the fields is invalid.
I ran into the same issue. After investigating, I've been informed it will be fixed in Vaadin 8.0.3, which is about to be released.
https://github.com/vaadin/framework/issues/8658

Notifying that all properties have changed on a ViewModel

I am working on a Silverlight application using V3 SP1 of MVVM Light Toolkit.
My application is fully French/English. All UI elements (buttons, labels, etc.) and all the data (models). I need dynamic language switching and this is fully implemented and works with anything coming from a resource file. What I am struggling with is the ViewModels.
The Models have language specific prperties (DescriptionEn, DescriptionFr) and an additional property call LocalizedDescription which uses the current culture to return call the language specific property.
When the language changes (via a button click) I raise and broadcast (via the Messenger) a property changed event.
In each of my ViewModels, I register to receive the property changed message for the language swap.
I want to notify all the properties of the ViewModel that something has changed.
From: http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.propertychanged.aspx
The PropertyChanged event can indicate all properties on the object have changed by using either null or String.Empty as the property name in the PropertyChangedEventArgs.
However, since the toolkit abstracts the raising of the changed event with RaisePropertyChanged(...) I cannot get this to work. I have also examined the source of the tookit and discovered that RaisePropertyChanged calls VerifyPropertyName(..) which in turn returns an error is the property does not belong to the ViewModel. I also noticed that the VerifyPropertyName method is attributed with Conditional("DEBUG"), but even if I choose the Release configuration, the ArgumentException("Property not found") is still raised.
Does anyone know of a way to get this to work using the toolkit aside from manually calling RaisePropertyChanged for every property of the ViewModel?
Follow-up:
Based on the comment from Simon, I attempted to create my own class that extends ViewModelBase. I looked at the source on CodePlex and decided to create a single method called RaiseAllPropertyChanged(). It would simply be a copy of the RaisePropertyChanged(string propertyName) but without the parameter and without the call to VerifyPropertyName(...). I cannot get it to work. Here is what I have.
public class ViewModelBaseExtended : ViewModelBase
{
protected void RaiseAllPropertyChanged()
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(String.Empty));
}
}
}
But I get a compiler error: The event 'GalaSoft.MvvmLight.ViewModelBase.PropertyChanged' can only appear on the left hand side of += or -=. This is a copy of the code that is used in the ViewModelBase.
Can someone offer some advice as to how to get this to work?
Solution:
I copied all the code from ViewModelBase into a new class. I then added the method RaisePropertyChanged() mentioned above which instantiates the PropertyChangedEventArgs class with String.Empty. This is now the new subclass for my ViewModels.
Thanks again to Simon for leading the way!
In case you're reading this in 2016, you can use ObservableObject and notify that all of the properties have changed by doing:
RaisePropertyChanged(string.Empty);
Unfortunately this is not possible with the current code-base of MVVMLight
In the short term your have 2 options:
User your own custom base class. And by custom base class I mean "Do not inherit from the MVVMLight class".
Download and compile MVVMLight in Release mode. This will force the "VerifyPropertyName" method to be excluded. Of course then you don't get the value of property name checks.
I am sure Laurent Bugnion will have this fixed soon.
A lighter solution to this problem would have been to override RaisePropertyChanged(string propertyName) in your class :
protected override void RaisePropertyChanged(string propertyName)
{
if (propertyName != null)
{
base.RaisePropertyChanged(propertyName);
}
else
{
var handler = PropertyChangedHandler;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(null));
}
}
}

I can't dispatch custom event from one module to another as it gives TypeError: Error #1034: Type Coercion failed:

I am trying to dispatch a custom event from one flex module to another.
The code which dispatch the event is as below
Application.application.Destination.child.dispatchEvent(
new AlgoEvent(AlgoEvent.GETFROMPARENT_LOCAL_EVENT));
here AlgoEvent is a custom event
on the other side the module which catches and handles the event has this code:
public function sendParametersToChild(e:AlgoEvent):void
{
//some codes
}
but when the statement Application.application.Destination.child.dispatchEvent(new AlgoEvent(AlgoEvent.GETFROMPARENT_LOCAL_EVENT)); is executed the debugger give the following run time exception:
TypeError: Error #1034: Type Coercion failed: cannot convert resources.events::AlgoEvent#4182239 to resources.events.AlgoEvent.
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.core::UIComponent/dispatchEvent()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\UIComponent.as:9298]
at components::Destination/sendTimeToChild()[E:\FlexProjects\MyApp\src\components\Destination.mxml:99]
at components::Destination/updateParameters()[E:\FlexProjects\MyApp\src\components\Destination.mxml:206]
at components::Destination/__CreateBasketButton_click()[E:\FlexProjects\MyApp\src\components\Destination.mxml:558]
I am not able to identify what is going wrong here.
Please help to solve this problem
This is my Event class
public class AlgoEvent extends Event
{
public static const GETFROMPARENT_LOCAL_EVENT:String = "getfromparent_local";
private var eventType:String;
public function AlgoEvent(eventType:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(eventType,bubbles,cancelable);
this.eventType=eventType;
}
}
While debugging am getting error in this funcion of UIComponent class
override public function dispatchEvent(event:Event):Boolean
{
if (dispatchEventHook != null)
dispatchEventHook(event, this);
return super.dispatchEvent(event);
}
Excaxtly this line gives the error: dispatchEventHook(event, this);
Import the AlgoEvent class in the main application and create a reference to it.
import resources.events.AlgoEvent;
private var dummyEvent: AlgoEvent;
Some explanations for this could be found here: Module domains
If your custom event doesn't carry any special event properties you could workaround your problem by using the standard Event class.
dispatchEvent(new Event(AlgoEvent.GETFROMPARENT_LOCAL_EVENT));
I had the same problem when dispatching, solved overriding two functions:
override public function clone():Event
{
return new AlgoEvent(type, bubbles, cancelable);
}
override public function toString():String
{
return formatToString("AlgoEvent","type"","bubbles","cancelable","eventPhase");
}
hope it helps out :)
Mr. splash suggested a solution which worked fro me:
Try to make the Custum Event (Algo Event in my case) class known to the main application.
I.e import it in the main application and create a variable of it..
And it works for a main reason>>when we try to communicate betwwen the modules using event dispatching what happens is :the modules are loaded at the run time but the classes like event classes are linked to the modules at the run time..
But the Event class is compiled before the modules are loaded..
application defines a Custum Event Class at compile time, and the module defines its own Custum Event Class when it is published. Then when the application is run, the Custum Event Class dispatched in the application doesn't match the one in the module
swf.
For the problem which is causing this error one can check the link:
http://www.kirupa.com/forum/showthread.php?t=320390
and also
http://www.jeffdepascale.com/index.php/flash/custom-events-in-loaded-swf-files/
Mate framework takes care of all this.
It gives you a global event bus, for all modules in your app.
http://mate.asfusion.com/
Try to override the clone() method in your customized Event,AlgoEvent.
Add the following code to your AlgoEvent.as class and try:
override public function clone():Event{
return new AlgoEvent(eventType,bubbles,cancelable);
}
HTH.
Your custom Event class should look like this:
public class AlgoEvent extends Event
{
public static const GETFROMPARENT_LOCAL_EVENT:String = "getfromparent_local";
public function AlgoEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
};
override public function clone():AlgoEvent
{
return new AlgoEvent(type, bubbles, cancelable);
};
};
You should use the Event's inherited type property instead of creating a new one.
Also, the UIComponent has it's own dispatchEvent method, so you don't have to create your own - only if it works differently to the inherited one.
Regards,
Rob
Okay, it must be said that what you're doing, from an architectural standpoint, is wrong. Calling Application.application is bad for so many reason, especially if you're then starting to go down the display tree. The second any of the children changes, your build is now broke, and you won't know that until runtime because it's a module.
What you need is an application framework. A way to increase complexity without decreasing maintainability. There are many out there, but my personal favorite is Parsley. I've used it on many very large projects with much success. The problem you're trying to solve right now, dispatching one event where the other module listens for it, is extremely trivial (can be done in about 3 lines of code).
I recommend you look it over as well as my presentation on an introduction to parsley.

Flex 4 Desktop App TooltipManager

I have a flex app that used to be an internet based app. In it I have a function that creates tooltip error messages when I tell it to create an error message. I pulled this app out verbatim to a desktop app and restructured things a bit to get it to run, but I did not mess with the core fundamentals of the mxml file that utilizes this aside from changing the root tag from a type of 's:Group' to 's:Window'
Everything runs correctly, but the tooltips are not displaying. I can't seem to figure out why, so I thought I'd run this by you guys. Here's the tooltip code (inline mxml code in the fx:script cdata tag):
import mx.controls.ToolTip;
import mx.managers.ToolTipManager;
public var errorTip:ToolTip;
private function createErrorMsg(errorMsg:String, object:Object):void {
if (errorTip){ToolTipManager.destroyToolTip(errorTip);};
errorTip = ToolTipManager.createToolTip(errorMsg,object.getBounds(root).x + object.width,object.getBounds(root).y) as ToolTip;
errorTip.setStyle("styleName", "errorTip");
errorTip.visible = true;
errorTip.enabled = true;
}
Basically, I pass the function a string and an object (text input, checkbox, button, etc...etc...) and it positions it and displays the error message. This fully works in my web version, but not in my desktop version.
Here's the code that instantiates the window:
var window:LoginWindow = new LoginWindow();
Window.systemChrome = NativeWindowSystemChrome.NONE;
Window.transparent = true;
Window.open(true);
Window.maximize()
Any ideas?
On a side note, I check to see if the errorTip exists at the beginning of the function and then destroy it so that the higher scoped variable 'errorTip' always equals the reference to the currently displayed error. This allows me to just destroy that error tip on form validation and then error check again, but it only allows one tooltip to be displayed at a time. Is there a better way to query the tooltip manager for all of it's currently displayed tooltips and destroy them all?
To resolve this you must change the line that calls 'createTooltip' to the following:
errorTip = ToolTipManager.createToolTip(errorMsg,object.getBounds(root).x + object.width,object.getBounds(root).y, null, object as IUIComponent) as ToolTip;
The idea behind this is that you can now just call 'createErrorMsg("myError",myComponent)' and it'll display an error message there, which you can then add another function:
private function clearError():void
{
if(errorTip) errorTip.visible = false;
}
to actually remove the error message. The down side of this is that it only displays one message at a time but I'll tweak it a bit to change that up. So here's my better resolution:
Error messages will (traditionally) only be displayed in a batch set. (Basically you will only have a handful of tooltips displayed at one point in time and they will all be related to the form you are on). This won't always be true, but that's not my concern here. I created a utility class that performs error message handling:
package Utils
{
import mx.controls.ToolTip;
import mx.core.IUIComponent;
import mx.managers.ToolTipManager;
public class ErrorUtils
{
private static var errorCache:Array = new Array();
public function ErrorUtils()
{
}
public static function createErrorMsg(errorMsg:String, object:Object):void {
var errorTip:ToolTip = ToolTipManager.createToolTip(errorMsg,object.getBounds(object.root).x + object.width,object.getBounds(object.root).y, null, object as IUIComponent) as ToolTip
errorTip.setStyle("styleName", "errorTip");
errorTip.visible = true;
errorTip.enabled = true;
var position:int = errorCache.length;
errorCache.push(errorTip);
}
public static function clearAllErrors():void
{
for each(var error:ToolTip in errorCache)
{
ToolTipManager.destroyToolTip(error);
}
}
}
}
To use the class you simply call ErrorUtils.createErrorMsg("my message",invalidComponent), and to clear the errors you simply call ErrorUtils.clearAllErrors()
That way if you have a form and you have a validation function for it, you simply call that function every time it comes back as invalid. The only thing that this is missing is any kind of way to clear a specific error, but to handle that you could place all of the creation calls inside of a master validation function that validates all the fields in your form, then call that each time you gain or loose focus on a field and call 'ErrorUtils.clearAllErrors()' at the beginning of the function.
BTW:
this is the source of the original tutorial I received to do this, I just abstracted it a bit
http://aralbalkan.com/1125
so credits go to the above site for the original code

TypeError: Error #1034: Type Coercion failed: cannot convert Object#1456c7b9 to mx.messaging.messages.IMessage

Im trying to connect a Flash client to BlazeDS. There has been some success with this from others using the vanilla BlazeDS setup. However I'm using the new Spring BlazeDS Integration from springsource and running aground.
The flash client actually seems to be working in that I can see the correct data in the body of the returned object, but for some reason unknown it fails casting as an IMessage. It fails in PollingChannel.as on this line with the subject line error
var messageList:Array = msg.body as Array;
for each (var message:IMessage in messageList) <--
On application load I register a whole bunch of classes like so
registerClassAlias( "flex.messaging.messages.RemotingMessage", RemotingMessage );
registerClassAlias("mx.messaging.messages.IMessage", IMessage);
etc..
my code is basically
var channelSet:mx.messaging.ChannelSet = new mx.messaging.ChannelSet();
var channel:mx.messaging.channels.AMFChannel = new AMFChannel("my-amf", "http://localhost:8400/SpringA/messagebroker/amf");
channelSet.addChannel(channel);
var consumer:mx.messaging.Consumer = new Consumer();
consumer.channelSet = channelSet;
consumer.destination = "simple-feed";
consumer.subscribe();
consumer.addEventListener(MessageEvent.MESSAGE, test);
private function test(event:IMessage)
{
trace("msg..");
// breakpoint never makes it here
}
I have a flex client which works 100% with same destination/channel.
The error in the title means that you, for some reason, got an object that is not implementing or extending the IMessage interface, therefore the loop can not cast it in this part:
for each (var message:IMessage in messageList){
Either you should somehow make sure that you don't add anything that is not extending or implementing IMessage, or check if the variable IS actually ext./imp. it. Also - if you want to do that, you will have to change the for each like this:
for each (var obj in messageList){
if (obj is IMessage){
var message:IMessage = obj as IMessage;
// DO STUFF HERE
}
}
Add this Object mapping:
registerClassAlias("flex.messaging.io.ObjectProxy", ObjectProxy);
If on your Java VO objects you have overridden the hashcode() method, this situation could happen.
Remove the hashcode() override (if you are able to).
See my blog for the backstory on how I discovered this. http://squaredi.blogspot.com/2013/12/remoting-landmine-without-stack-trace.html
I had the same error when trying to send an actionscript object to the backend. My problem was that my c# equivalent object was missing an public parameterless constructor.

Resources