I'm trying to disable the draginitiator, that's the semi-transparant object when you drag something.
Does anyone know how to do this?
EDIT Code
<s:List id="dg_ads" top="75" bottom="0" width="100%" borderVisible="false"
dragEnabled="true" dropEnabled="true" dragMoveEnabled="true"
dragComplete="dg_ads_dragCompleteHandler(event)"
doubleClickEnabled="true" doubleClick="dg_ads_doubleClickHandler(event)"
contentBackgroundColor="#FFFFFF">
<s:layout>
<s:TileLayout useVirtualLayout="false" clipAndEnableScrolling="false"
horizontalGap="5" verticalGap="5" />
</s:layout>
</s:List>
Create a custom list class that extends spark List and override createDragIndicator() method.This is method is used by the DragManager to create the dragProxy(The image you will see when a drag operation is in progress).
override public function createDragIndicator():IFlexDisplayObject
{
var dragIndicator:UIComponent;
dragIndicator = new UIComponent();
dragIndicator.width = 0;
dragIndicator.height = 0;
return dragIndicator;
}
Use the custom list instead of spark List in your application
From this question:
Try:
event.dragInitiator.visible = false;
or create your own version of the DragProxy class and remove the portions you don't want.
Related
I am using a Repeater in an Accordian which does not appear to see a single element in userArray. If I add another entry to userArray then the Repeater works fine.
Thoughts??
private function currUsersServiceHandler(event:ResultEvent):void{
if (event.result.currentUsers != null)
{
if (event.result.currentUsers.user is ArrayCollection) // if more than one elements are present
{
usersArray = event.result.currentUsers.user;
}
else if (event.result.currentUsers is ObjectProxy)
{ //FIXIT usersArray populate by following line has some issue
usersArray = new ArrayCollection(ArrayUtil.toArray(event.result.currentUsers));
}
}
}
<mx:HTTPService id="currUsersService" url="currUsers.xml" result="currUsersServiceHandler(event)"/>
<mx:Accordion includeIn="UserList" x="10" y="10" width="554" height="242" >
<mx:Repeater id="rep" dataProvider="{usersArray}">
<mx:Canvas width="100%" height="100%" label="{rep.currentItem.firstName}" >
<mx:HBox>
<s:Label text="{rep.currentItem.firstName}"/>
<s:Label text="{rep.currentItem.lastName}"/>
<mx:/HBox>
</mx:Canvas>
</mx:Repeater>
</mx:Accordian>
Edit:
There is another thing I have just noticed i.e. that the accordian does show a single tab (when Array has a single element) but it's not labeled with the first name which I am setting. If I enter another user, two tabs appear and both are labeled with names I am setting. The first tab appears labeled too then.
It makes no sense to me that this would not work with 1 item in the dataProvider, but would work with two.
That said, tThis approach strikes me as convoluted and I tend to stay away from using repeaters at all. I would suggest a different approach.
First create a component to display your data. You can reuse you're existing code. Conceptually something like this:
<mx:Canvas width="100%" height="100%" >
<mx:Script><[[
public var user : Object;
]]></mx:Script>
<mx:HBox>
<s:Label text="{user.firstName}"/>
<s:Label text="{user.lastName}"/>
<mx:/HBox>
</mx:Canvas>
Then in your original component create the new instance of the component in ActionScript:
for each(var myUserObject : Object in usersArray){
var newUserDisplayObject : UserDisplayObject = new UserDisplayObject();
newUserDisplayObject.user = myUserObject;
newUserDisplayObject.label = myUserObject.firstName
accordian.addChild(newUserDisplayObject);
}
I have encountered somthing a little strange in flex, possibly somthing im doing wrong
but im not sure.
In two cases which i have noticed, when there is only 1 item in a s:List or s:DropDownList
for some reason when using list.selectedItem it appears as null. Using requireSelection="true"
i know this isnt the case.
Has anyone else seen anything similar? or am i doing it completly wrong?
Thanks
Jon
Edit: In the code bellow it happens when clicking the edit document, which calls the open edit method
------------ Added Code ---------------------------
I have removed small portions to make it more readable
<s:TitleWindow width="486" height="300" title="Document Store"
xmlns:tmsbean="services.tmsbean.*"
close="close()">
<fx:Declarations>
<s:CallResponder id="getAllAttachedDocumentsResult"/>
<tmsbean:TMSBean id="tMSBean" showBusyCursor="true"/>
<s:CallResponder id="removeDocumentLinkResult" result="getDocumentList()"/>
</fx:Declarations>
<fx:Script>
<![CDATA[
private static var documentStoreView:DocumentStoreView = null;
[Bindable]
private var attachedToMenomic:String;
public static function getInstance():DocumentStoreView
{
if(documentStoreView == null){
documentStoreView = new DocumentStoreView();
DocumentForm.getInstance().addEventListener(DocumentFormEvent.DOCUMENT_ATTACHED,documentStoreView.getDocumentList);
}
return documentStoreView;
}
public function open(menomic:String,parent:DisplayObject):void
{
attachedToMenomic = menomic;
getDocumentList();
PopUpManager.addPopUp(documentStoreView,parent,true);
PopUpManager.centerPopUp(documentStoreView);
y = y - 80;
}
public function close():void
{
PopUpManager.removePopUp(documentStoreView);
}
private function getDocumentList(evt:DocumentFormEvent = null):void
{
getAllAttachedDocumentsResult.token = tMSBean.getAllAttachedDocuments(attachedToMenomic);
}
private function openEdit():void{
var editDsi:DocumentStoreItem = documentList.selectedItem as DocumentStoreItem;
Alert.show(editDsi.documentName);
DocumentForm.getInstance().openInEditMode(editDsi,this);
}
]]>
</fx:Script>
<s:VGroup left="10" top="10" right="10" bottom="10">
<s:List width="100%" height="100%" id="documentList" itemRenderer="com.documentStore.DocumentItemListRenderer"
dataProvider="{Utilitys.toArrayCollection(getAllAttachedDocumentsResult.token.result)}" />
<s:HGroup horizontalAlign="right" width="100%">
<s:Button label="Attach Document" click="{DocumentForm.getInstance().open(attachedToMenomic,this)}"/>
<s:Button label="Edit Document" click="openEdit()"/>
</s:HGroup>
</s:VGroup>
</s:TitleWindow>
By default Spark's DropDownList displays a prompt if selectedIndex is -1, which will be the case if requireSelection is false and you have not otherwise set the list to a specific item. This would correspond with selectedItem being null.
The Spark ComboBox does something similar but it has a TextInput as you can type into it.
I'm going spare trying to figure out the "correct" way to embed a ComboBox inside a Flex (3.4) DataGrid. By Rights (e.g. according to this page http://blog.flexmonkeypatches.com/2008/02/18/simple-datagrid-combobox-as-item-editor-example/) it should be easy, but I can't for the life of me make this work.
The difference I have to the example linked above is that my display value (what the user sees) is different to the id value I want to select on and store in my data provider.
So what I have is:
<mx:DataGridColumn headerText="Type" width="200" dataField="TransactionTypeID" editorDataField="value" textAlign="center" editable="true" rendererIsEditor="true">
<mx:itemRenderer>
<mx:Component>
<mx:ComboBox dataProvider="{parentDocument.transactionTypesData}"/>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
Where transactionTypesData has both 'data' and 'label' fields (as per what the ComboBox - why on earth it doesn't provide both a labelField and idField I'll never know).
Anyway, the above MXML code doesn't work in two ways:
The combo box does not show up with any selected item.
After selecting an item, it does not store back that selected item to the datastore.
So, has anyone got a similar situation working?
While Jeff's answer is a partial answer for one approach for this (see http://flex.gunua.com/?p=119 for a complete example of this being used to good effect), it isn't as general as I wanted.
Thankfully, I finally found some great help on Experts Exchange (the answers by hobbit72) describes how to create a custom component that works in a grid as a ItemRenderer.
I've extended that code to also support using the combo box as an ItemEditor as well. The full component is as follows:
<?xml version="1.0" encoding="utf-8"?>
<mx:ComboBox
xmlns:mx="http://www.adobe.com/2006/mxml"
dataChange="setSelected()"
change="onSelectionChange(event)"
focusEnabled="true">
<mx:Script>
<![CDATA[
import mx.events.DataGridEvent;
import mx.events.ListEvent;
import mx.controls.dataGridClasses.DataGridListData;
private var _ownerData:Object;
private var _lookupField:String = "value";
// When using this component as an itemEditor rather than an itemRenderer
// then set ' editorDataField="selectedItemKey"' on the column to
// ensure that changes to the ComboBox are propogated.
[Bindable] public var selectedItemKey:Object;
public function set lookupField (value:String) : void {
if(value) {
_lookupField = value;
setSelected();
}
}
override public function set data (value:Object) : void {
if(value) {
_ownerData = value;
setSelected();
}
}
override public function get data() : Object {
return _ownerData;
}
private function setSelected() : void {
if (dataProvider && _ownerData) {
var col:DataGridListData = DataGridListData(listData);
for each (var dp:Object in dataProvider) {
if (dp[_lookupField] == _ownerData[col.dataField]) {
selectedItem = dp;
selectedItemKey = _ownerData[col.dataField];
return;
}
}
}
selectedItem = null;
}
private function onSelectionChange (e:ListEvent) : void {
if (selectedItem && _ownerData) {
var col:DataGridListData = DataGridListData(listData);
_ownerData[col.dataField] = selectedItem[_lookupField];
selectedItemKey = selectedItem[_lookupField];
}
}
]]>
</mx:Script>
</mx:ComboBox>
Using this component is straight forward. As an ItemRenderer:
<mx:DataGridColumn headerText="Child" dataField="PersonID" editable="false" textAlign="center">
<mx:itemRenderer>
<mx:Component>
<fx:GridComboBox dataProvider="{parentDocument.childrenData}" labelField="Name" lookupField="PersonID" change="dispatchEvent(new mx.events.DataGridEvent(mx.events.DataGridEvent.ITEM_FOCUS_OUT, true, true))"/>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
Using this component is straight forward. And as an ItemEditor:
<mx:DataGridColumn labelFunction="lookupChildName" headerText="Child" dataField="PersonID" editable="true" editorDataField="selectedItemKey">
<mx:itemEditor>
<mx:Component>
<fx:GridComboBox dataProvider="{parentDocument.childrenData}" labelField="Name" lookupField="PersonID" change="dispatchEvent(new mx.events.DataGridEvent(mx.events.DataGridEvent.ITEM_FOCUS_OUT, true, true))"/>
</mx:Component>
</mx:itemEditor>
</mx:DataGridColumn>
Note that when using it as an ItemEditor, a custom labelFunction (that looks up the Name from the PersonID in my case) must be used, otherwise you only see the key in the grid when the field isn't being edited (not a problem if your keys/values are the same).
Note that in my case, I wanted the item focus out event to propogate up to provide immediate feedback to the user (my DataGrid has itemFocusOut="handleChange()"), hence the change event creating an ITEM_FOCUS_OUT event.
Note that there are probably simpler ways to have a ComboBox as an ItemEditor when you don't mind the ComboBox only shown when the user clicks on the cell to edit. The approach I wanted was a generic way to show a combo box in a DataGrid for all rows, and being editable and with decent event propogation.
The easiest way to add itemRenderers to DataGrids is to make a custom MXML component. In your case make a canvas, HBox, or VBox as the custom component and add the combobox as a child.Set the dataProvider on the dataGrid itself and assign the itemRenderer to the column, and then override the set data function of the itemRenderer to access all data from the given data provider for that instance as seen below:
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
override public function set data(value:Object):void{
trace(value.data);
trace(value.name);
}
]]>
</mx:Script>
<mx:ComboBox width="100%" height="100%" id="myComboBox"/>
</mx:HBox>
This method will be called for each instance of the itemRenderer
In my case I used a spark datagrid where one of the columns has an ItemRenderer that utilises a DropDownListBox. My problem was that when my item list change, the DropDownLists doesn't get updated with the new dataProvider. To solve this, I had to pass the dataProvider for the DropDownListBox as part of the data (of the ItemRenderer), and then by overriding the setter of the data to just assign the DropDownlListBox's dataProvider. Probably a bit of overhead, but if someone have a better solution, please let me know:
<s:GridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
override public function set data(v : Object) : void {
super.data = v;
if (v == null)
return;
dropDown.dataProvider = data.dataProvider;
}
]]>
</fx:Script>
<s:DropDownList id="dropDown" width="100%" height="100%" dataProvider="{data.dataProvider}" labelField="name"/>
I have a Flex question. I was wondering, if I have two list boxes, and I want to drag and drop between both of them, how do I prevent the user from dragging onto the same list (thus duplicating the item? I cannot have a situation where that is the case. Thanks guys
Haven't tested it but I guess something like this should work:
Listen to dragStart event on both lists and set a source variable depending on event.target. Now listen to the dragDrop event on both lists and call event.preventDefault() if the source is same as the target.
Here are some simple functions I made while building a working application I used to wrap my head around flex drag and drop. I was looking for a way to have multiple lists with drag and drop functionality that would not interfere with each other. Also, I didn't want to deal with copying list data around.
private function onlyAllowMoveDragOverHandler(event:DragEvent):void {
event.preventDefault();
event.currentTarget.showDropFeedback(event);
DragManager.showFeedback(DragManager.MOVE);
}
private function allowDropOnlyIfInitiatorEqualsComponent(event:DragEvent, component:IUIComponent):void {
event.preventDefault();
if (event.dragInitiator == component) {
DragManager.acceptDragDrop(event.target as IUIComponent);
}
else {
DragManager.showFeedback(DragManager.NONE);
}
}
And I in use in my mxml:
<mx:List
x="10"
y="170"
id="availableLangsList"
dataProvider="{availableLangs}"
width="100"
height="200"
dragEnabled="true"
dragMoveEnabled="true"
dropEnabled="true"
dragOver="onlyAllowMoveDragOverHandler(event);"
dragEnter="allowDropOnlyIfInitiatorEqualsComponent(event, selectedLangsList);"
dragComplete="selectedLangs.refresh();"
/>
<mx:Label x="129" y="153" text="list 4"/>
<mx:List
x="129"
y="170"
id="selectedLangsList"
dataProvider="{selectedLangs}"
width="100"
height="200"
dragEnabled="true"
dragMoveEnabled="true"
dropEnabled="true"
dragOver="onlyAllowMoveDragOverHandler(event);"
dragEnter="allowDropOnlyIfInitiatorEqualsComponent(event, availableLangsList);"
dragComplete="availableLangs.refresh();"
/>
I found the solution, which im not sure would work for anyone else. I basically had in my two lists:
`
<mx:List id="srcList" dataProvider="{_source}"
allowMultipleSelection="true"
enabled="{enabled}"
labelField="{labelField}"
iconFunction="iconFunction"
dragEnabled="true"
dropEnabled="true"
dragDrop="doDragDrop(event);"
width="100%"
height="100%"
/>
</mx:VBox>
<mx:VBox paddingTop="50">
<mx:Button label="->" enabled="{enabled}" click="add()"/>
<mx:Button label="<-" enabled="{enabled}" click="rem()"/>
</mx:VBox>
<mx:VBox width="100%" height="100%">
<mx:Label text="{right_col_heading}" />
<mx:List id="dstList" dataProvider="{_destination}"
allowMultipleSelection="true"
enabled="{enabled}"
dragEnabled="true"
dropEnabled="true"
dragDrop="doDragDrop(event);"
width="100%"
height="100%"
labelField="{labelField}"
iconFunction="iconFunction"
verticalAlign="center"
/>`
I basically added a dragMoveEnabled = "true" to both lists and now basically not re-add to the same list an item of itself, but just move the order (which doesnt matter to me as its a soap send and the back-logic would put it in the correct order anyway).
In my case, I used a HashCollection (which extends ArrayCollection) [just google it, you'll find the component]. The dataprovider is bind to this has collection. You would add items to the collection with: dataprovider.put (key, object) instead of dataprovider.addItem(object).
The "hash" will ensure uniqueness in the collection. SO, even if the user drag-drop something that already exists in the hash, the original value would get replaced with the new object (but it wouldn't matter because it's the same value).
The "key", however, must be unique.... otherwise, the hash idea won't work.
Thanks Brice, Those functions were helpful.
For them to work in Spark Lists just update the first function as follows with createDropIndicator instead of showDropFeedback and stop passing the event.
private function onlyAllowMoveDragOverHandlerS(event:DragEvent):void {
event.preventDefault();
event.currentTarget.createDropIndicator();
DragManager.showFeedback(DragManager.MOVE);
}
How would I display a backgroundImage on a List when the List is empty?
At the moment the list is populated when items are dropped inside after a drag-and-drop but I would prefer a solution that checks for any change to the data to determine if the list is empty.
The List inherits a backgroundImage from its ScrollControlBase but what would be the best way to make it appear when the list is empty and disappear when an item is added.
Any suggestions?
Thanks!
In the past, I've done it with states for a component. Quick and dirty example would be something like this in your custom component:
<mx:List currentState="{(listItemsDataProvider.length > 0) ? 'HasItemsState' : 'NoItemsState'}">
// anything else you need
</mx:List>
and of course creating those states in the component, with the NoItemsStates changing the background image, or if your component is a container, like a Canvas, then you can have the state not display the List at all.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable] public var listItems:ArrayCollection = new ArrayCollection();
private function removeAllItemsFromList():void
{
this.listItems.removeAll();
backgroundCheck()
}
private function addItemToList():void
{
this.listItems.addItem({data:null,label:"test"});
backgroundCheck()
}
private function backgroundCheck():void
{
if(this.listItems.length>0)
{
this.myList.setStyle("backgroundImage", null)
}
else
{
this.myList.setStyle("backgroundImage", "me.png")
}
}
]]>
</mx:Script>
<mx:VBox width="100%" height="100%">
<mx:List id="myList" width="100%" height="100%" backgroundImage="me.png" dataProvider="{this.listItems}"/>
<mx:HBox width="100%">
<mx:Button id="addItemButton" click="addItemToList()" label="add item"/>
<mx:Button id="removeItemsButton" click="removeAllItemsFromList()" label="remove all items"/>
</mx:HBox>
</mx:VBox>
</mx:Application>
This is how I would approach it, checking the dataProvider length. In your case you'd do so when the drop is complete.
You could extend the List control and override updateDisplayList(). Draw the backgroundImage if dataProvider.length == 0 else call super.updateDisplayList() to get normal List behavior. This will make the new List control easy to reuse if you need to.
Use the same property, set the image to null when you have some data. You may take a look at custom ItemRenderers as well.