I have a flex application with two objects: a parent widget (called an IBaseWidget) and a child widget (called a HelperWidget2). When the user clicks on a help link, a helper widget is loaded into the list of base widgets and then displayed for the user.
However, when I try to access this child widget by casting the base widget in the collection to the child widget type, the child widget returns null and I am unable to work with the widget.
The following snippet correctly returns the widget ID of the newly added widget and dispatches an event to load the widget:
var id:Number = WidgetManager.getInstance().getWidgetId("Helper");
ViewerContainer.dispatchEvent(new AppEvent(AppEvent.WIDGET_RUN, id, openQuickQueryCanvas));
Once the widget is loaded, a callback function called openQuickQueryCanvas() attempts to do another action with the helper widget:
private function openQuickQueryCanvas():void{
var id:Number = WidgetManager.getInstance().getWidgetId("Helper");
var bWidget:IBaseWidget = WidgetManager.getInstance().getWidget(id) as IBaseWidget;
var helperWidget:HelperWidget2 = bWidget as HelperWidget2;
if(helperWidget != null){
helperWidget.quickQueryCanvas.dispatchEvent(new MouseEvent(MouseEvent.CLICK));//fire an event to open the quick query canvas
}
}
The problem is that helperWidget above always returns null, meaning the cast isn't successful. This doesn't make sense to me, because bWidget is of type HelperWidget2.
Any thoughts? I'm stumped...
First off, make sure that HelperWidget2 implements IBaseWidget like so
public class HelperWidget2 implements IBaseWidget
Second, I would suggest using the is keyword instead of casting and checking for null:
private function openQuickQueryCanvas():void {
var id:Number = WidgetManager.getInstance().getWidgetId("Helper");
var bWidget:IBaseWidget = WidgetManager.getInstance().getWidget(id) as IBaseWidget;
if(bWidget is HelperWidget2)
{
HelperWidget2(bWidget).doWhatever();
}
}
Cast the returning instance as an object, instead of HelperWidget2. You won't have intellisense for the methods at design time, but more importantly, it won't be null at run time.
var bWidget:Object = WidgetManager.getInstance().getWidget(id);
bWidget.doWhatever();
Related
I know an ItemRenderer is a ClassFactory and that you can use the newInstance method of ClassFactory to get an instance of the ItemRenderer. My question, however, is is it possible to use methods of the ItemRenderer without using ClassFactory.newInstance()?
In my case I can't use this newInstance method because it doesn't keep the state.
Is there any way I can do this? Thanks!
An ItemRenderer is a component, like any other. The itemRenderer property of a list based class has a value of a ClassFactory. If you have a reference to an instance of the itemRenderer component, you can call methods on it.
You cannot call a method on any component if an instance if that component instance has not been created yet. So to call a method on an itemRenderer without using ClassFactory.newInstance() you must manually create your own instance using the new keyword.
You might want to implement the ItemRenderer as smart as it is needed to recreate the state depending in the data being set. On the other hand, make sure that the data contains everything needed. You barely want to interact with the renderers in a different scope then the renderer itself.
If it should necessary, a DataGroup dispatches a RendererExistence event when a renderer is added.
private function newList():List {
const list:List = new List();
list.addEventListener(FlexEvent.INITIALIZE, list_initializeHandler);
return list;
}
private function list_initializeHandler(event:FlexEvent):void {
const listBase:ListBase = ListBase(event.target),
dataGroup:DataGroup = listBase.dataGroup;
dataGroup.addEventListener(RendererExistenceEvent.RENDERER_ADD, dataGroup_rendererAddHandler);
dataGroup.addEventListener(RendererExistenceEvent.RENDERER_REMOVE, dataGroup_rendererRemoveHandler);
}
private function dataGroup_rendererAddHandler(event:RendererExistenceEvent):void {
// renderer added
}
private function dataGroup_rendererRemoveHandler(event:RendererExistenceEvent):void {
// renderer removed
}
This is the way to go if you need to reference single item renderer instances.
Do you mean static functions and variables?
If you define a function (or variable, or const) as static, it is accessible via the class name, so you could define
class MyClass {
public static const className:String="MyClass.className (const)";
public static function getClassName():String {
return "MyClass.getClassName (function)";
}
}
trace(MyClass.className); //prints "MyClass.className (const)"
trace(MyClass.getClassName()); //prints MyClass.getClassName (function)
I need to add multiple child objects to an existing parent Object. I am instantiating my parent object and sets it Key/Id in my UI processing layer(to which my child objects will be added).
Parent parenttoModify = new Parent();
parenttoModify.Parent_Id = 5; //this comes from some Index of a dropdown or a key column of a grid, i Have put a dummy value here for example
parenttoModify.Children.Add(child);
parenttoModify.Children.Add(child2);
DataAccess.ModifyParent(parenttoModify);
In my data access layer I have a method like :
public static bool ModifyParent(Parent parent)
{
int recordsAffected=0;
using (TestEntities testContext = new TestEntities())
{
testContext.Parents.Attach(parent);
var parentEntry = testContext.ObjectStateManager.GetObjectStateEntry(parent);
parentEntry.ChangeState(System.Data.EntityState.Modified);
recordsAffected = testContext.SaveChanges();
}
return recordsAffected > 0 ? true : false;
}
I get an error when testContext.Parent.Attach(parent) is called. It says:
Object with same key already exist.
I am not sure why is this happening since i am not adding a parent object, I am just attaching it and adding child objects within it.
Any idea where I am going wrong?
Where do you add childs? I guess you are not showing all code. When you call Attach or AddObject EF always attaches or adds all entities from object graph which are not known (tracked) to context at the moment. The exception says that some entity - probably parent - is already tracked by the context. So you have either:
Used shared context (you are creating a new instance in ModifyParent so it should not be a case)
Load parent from the context first in ModifyParent
Called Attach or AddObject on any child before attaching parent.
All these operations lead to the exception you are receiving.
If I have a flex component that is a general popup, it's basically just a white popup that I pass an Array named "modules" to.
For instance:
var array:Array = ["mainArticle","title"];
or
var array:Array = ["creditCard"];
These are two examples that I might pass in. The first one would add my modules to the popup so the popup will be used for editing an "article." The second would add the Credit Card Change module, which would be a form that would allow the user to update their credit card information.
My question resides in the dataProvider for this popup. If I am passing in the article updater, I need a dataProvider that contains information like "font," "color," "size," etc. If I am passing in the credit card updater, I need a dataProvider that contains information like "number," "securit code," "expiration date," etc.
I could have a dataProvider class that has all of the information and only sets what I need, but it could get huge if I did something like:
public class myDataProvider {
public var mainTextFont:String;
public var mainTextSize:int;
public var mainText:String;
public var cardNumber:String;
public var cardExpiration:Date;
public var cardSecurity:String;
}
This is sort of an abstract idea, but I am looking for a solution that allows me to give my popup dataproviders without using one central dataProvider that would have a copy for every possible situation.
Thanks!
The simplest way to approach this is to create different dataProviders, depending on the class. My main popup has a "ModuleList" (custom list of strings) and it adds "modules" (not an actual flex module) to itself, giving each one the correct type of dataProvider.
public var recipientList:RecipientList;
private function setupModules():void {
for each( var s:String in moduleList ){
switch( s ){
case 'recipients':
var recipients:Recipients = new Recipients();
recipients.list = recipientList;
break;
case 'article':
// Article Logic
break;
case 'creditCard':
// Credit Card Logic
break;
}
}
}
This is just a generic idea, I was wondering what the best way to do this was, and this is how I decided to approach it.
Would you consider use a generic Object class and wrap any param you need in the Object?
var data:Object = new Object();
data.mainTextFont = "";
data.mainTextSize= "";
data.mainText= "";
For component, you may also consider use "State" value to control the display/layout.
Hope this helps.
I have an ArrayCollection we'll call "Items". It is essentially a flat collection of hierarchical data (each item has Parent and Children properties). I want an AdvancedDataGrid to display the data in hierarchical form, so essentially I could just do this and it would display fine:
// Note: RootItems would be an ArrayCollection that is updated so only the
// top level items are within (item.Parent == null).
var hd:HierarchicalData = new HierarchicalData(model.RootItems);
var hcv:HierarchicalCollectionView = new HierarchicalCollectionView(hd);
myDataGrid.dataProvider = hdc;
This works, but I want to be able to see updates in myDataGrid when the Items collection is updated (since RootItems won't pick up updates to any children, only the top level tasks). Is there any easy way to do this? I'm guessing I'll have to create a class that extends HierarchicalData and somehow alert it when Items changes, but that sounds like it'll be pretty slow. Thanks in advance for any help you can provide!
You have two options to solve this problem. Either you create your own implementation of IHierarchicalData (it doesn't have to extend HierarchicalData and in this particular case there won't be much code you can reuse) or you change the way you handle your data a little bit so that it fits into the standard use case:
[Bindable] // make it bindable so that the HierarchicalCollectionView gets notified when the object changes
class Foo // your data class
{
// this constructor is needed to easily create the rootItems below
public function Foo(children:ArrayCollection = null)
{
this.children = children;
}
// use an ArrayCollection which dispatches an event if one of its children changes
public var children:ArrayCollection;
// all your other fields
}
// Create your rootItems like this. Each object can contain a collection of children
// where each of those can contain children of its own and so forth...
var rootItems:ArrayCollection = new ArrayCollection([
new Foo(
new ArrayCollection([
new Foo(),
new Foo(),
new Foo(
new ArrayCollection([
// ...
]),
new Foo()
])
),
new Foo(
// ...
),
// ...
]);
// Create the HierarchicalData and HierachicalCollectionView
var hd:IHierarchicalData = new HierarchicalData(rootItems);
[Bindable]
var hcv:IHierarchicalCollectionView = new HierarchicalCollectionView(hd);
Then you can use hcv as dataProvider in your ADG and use its methods to add and remove items. The ADG will refresh whenever you add, remove or update an item.
I suggest you do it the standard way unless that's really not possible.
I am trying to add some Sprite objects as the contents of an array, and I would like to be able to "clear" them from the stage. I would assume that if there are loaders involved, I need to do
_imgArray[i].close();
_imgArray[i].unload();
And if I am using a sprite, I can do:
removeChild(_imgArray[i]);
None of the above work. WHY???
For an example and/or description of how I am setting this up, see Joel's post here
...but note that he hasn't included a reference for deleting them from view.
Currently I try:
for(i = 0; i < _localXML.length(); i++)
{
var tmp:BMLink = new BMLink(_localXML[i], _bw, _bh, i);
_imgArray[i] = tmp;
_imgArray[i].x = (_bw + _mainpad) * i;
_base.addChild(_imgArray[i]);
}
But this doesn't work.
I would love it if someone could explain to me why this wouldn't be proper syntax.
The class instances that are populating the array are all extending sprite, but they have their own individual loaders inside w/ progress events etc.
jml
OK; I finally figured it out through a bunch of trial and error.
It seems that I was attempting to remove the child of my main class sprite (this) rather than the sub-sprite that I had added the children to.
Sorry for the noise, but for the record, if you find that you can't do
this.removeChild(_imgArray[i]);
it's not because you don't have the correct syntax, but because you might not have an
_imgArray[i]
at that particular point of your display list hierarchy... so...
_base.removeChild(_imgArray[i]);
...worked in this case.
jml
You can make an Interface IDestroy for example with a destroy method who will manage all cleaning/removing stuff :
public interface IDestroy{
function destroy():void;
}
public class MySprite extends Sprite implements IDestroy {
..
public function destroy():void{
// remove events
..
// remove loader
..
//remove from parent
if (parent!==null){
parent.removeChild(this);
}
// etc.. more cleaning
}
}
then when you have an object who is an instance of IDestroy you can call the destroy method
if (myObject is IDestroy){
IDestroy(myObject).destroy();
}
or another way
var id:IDestroy=myObject as IDestroy;
if (id!==null)
id.destroy();
Edit:
I don't understand why any of the method i gave you in the comment will not work but _base.removeChild(_imgArray[i]) will :
addChild and removeChild accept only a DisplayObject as a parameter, so if you can do _base.addChild(_imgArray[i]) it means that _imgArray[i] inherits from DisplayObject and _imgArray[i] has a parent.
So var myDisplayObject:DisplayObject=_imgArray[i] as DisplayObject; will not return null and you will be able todo myDisplayObject.parent.removeChild(myDisplayObject); which is a general approach to your problem without relying on your _base DisplayObjectContainer (MovieClip/Sprite/...)