How to dynamically load image for drag and drop? - apache-flex

I am implementing drag and drop from a DataGrid onto a List in a Flex 3 AIR application. I would like to have the drag image be a photo (jpg) referenced by a String field in the data grid item, named 'imagePath'. I'm having trouble getting the image to show up during dragging. I have triple checked that it is not because of an invalid path to the image. I have tried Image's source() and load() methods in every way I can think of. I am calling this method 'dragCurrentToList(event)' on a mouseDown event.
private function dragCurrentToList(event:MouseEvent):void
{
var current:Object = event.currentTarget.selectedItem;
var dragImg:Image = new Image();
dragImg.load(current.imagePath);
dragImg.width = 100;
dragImg.width = 100;
var dsource:DragSource = new DragSource();
dsource.addData(current, 'record');
DragManager.doDrag(event.currentTarget as DataGrid, dsource, event, dragImg);
}
This works perfectly if I set the image source to the following bindable variable but I don't want to hardcode the image name.
[Bindable]
[Embed(source='assets/icons/default.jpg')]
public var dragIcon:Class;
...
dragImg.source = dragIcon
...

In your dragCurrentToList method, why are you loading the image instead of just specifying the source attribute as the URL to the image?
http://livedocs.adobe.com/flex/3/langref/mx/controls/SWFLoader.html#source
private function dragCurrentToList(event:MouseEvent):void
{
var current:Object = event.currentTarget.selectedItem;
var dragImg:Image = new Image();
dragImg.source = current.imagePath;
dragImg.width = 100;
dragImg.width = 100;
var dsource:DragSource = new DragSource();
dsource.addData(current, 'record');
DragManager.doDrag(event.currentTarget as DataGrid, dsource, event, dragImg);
}
Also make sure you are responding to the dragStart event. ( http://livedocs.adobe.com/flex/3/langref/mx/core/UIComponent.html#event:dragStart ). And I believe instead of accessing the DragManager class; you should simply modify the dragSource property of the dragStart event.

Related

Flex 4 Drag-n-Drop with custom DragProxy

I'm doing the drag an drop of an ItemRenderer manually (DataGrid) and want to know how to generate a custom DragProxy of a component that hasn't been added to the display list.
I tried something like this but didn't work:
private function doDrag(event:MouseEvent):void
{
var dragSource:DragSource = new DragSource();
dragSource.addData(data, 'dnd_format');
//var bm:Bitmap = new Bitmap(ImageSnapshot.captureBitmapData(this));
var btn:Button = new Button();
btn.label = 'New Button';
var bm:Bitmap = new Bitmap(ImageSnapshot.captureBitmapData(btn));
var dragProxy:Image = new Image();
dragProxy.source = bm;
DragManager.doDrag(this, dragSource, event, dragProxy,0,0, 0.6);
}
So, I want to be able to create the DragProxy using a component, the button is just an example.
Any ideas?
My guess is that this is not working because you are trying to get a bitmap from a component that was just created and has not been added to the stage. I would try testing this code with using an embedded image as the drag proxy first. If that works, then try getting a bitmap from a component that exists on the stage. My guess is that both cases will work.

How do i know that which button is clicked in flex?

for (iss = 0; iss < listOfProductIds2.length; iss++)
{
// Alert.show(listOfProductIds2[iss]);
var productMain:VBox=new VBox();
var p1:HBox=new HBox();
var l1:Label=new Label();
var b1:Button=new Button();
var spacer:Spacer=new Spacer();
spacer.width=300;
b1.label="Remove";
b1.setConstraintValue("id","");
b1.addEventListener(MouseEvent.CLICK,removeProduct);
l1.text="Product "+iss;
p1.setActualSize(500,500);
p1.addChild(l1);
p1.addChild(spacer);
p1.addChild(b1);
productMain.addChild(p1);
}
function removeProduct(event:MouseEvent):void
{
// How do i know which button is clicked
}
Use event.currentTarget (instead of event.target) because event.target might be the Label component or some styling component within the button, but currentTarget is assured to be the object with which the listener was registered.
To get a handle to the button that was clicked you can just cast the currentTarget to a button.
function removeProduct(event:MouseEvent):void
{
var b1:Button = Button(event.currentTarget);
}
The method setConstraintValue is for setting layout constraints, not setting id. The id property is used by mxml for creating variable names for objects. You can get/set id as you would get/set any other property (say width) - but neither have I seen anyone doing that nor do I see any need to do that in the first place.
event.target should point to the Button you clicked on, shouldn't it ? However you should probably give ids to the buttons to be able to differenciate them (since you create them dynamically.)
Look at event.target.
If ids are assigned dynamically as in the example given b1.id = "button_" + listOfProductIds2[iss]
Then the function that processes the click event would look at the currenttarget, and what I usually do is do a string replace on the part of the id that you know is not dynamic like "button_" with "", which leaves you with the name of the product.

Getting Flex TileList image source

I have a TileList that's loaded with data from Flickr. The tilelist uses an imageRenderer to make a bunch of thumbnails.
I'm trying to create a custom drag and drop function, but I want to get the image source of the tilelist mouseEvent target. Here's what the code looks like for the drag handler:
public function onPicMouseDown(e:MouseEvent):void {
var tileList:TileList = TileList(e.currentTarget);
var item:Object = Object(tileList.selectedItem);
var source:DragSource = new DragSource();
var dragView : Image = new Image();
dragView.source = tileList.selectedItem.source;
DragManager.doDrag(
rowRenderer,
source,
e,
dragView
);
}
But tileList.selectedItem doesn't have a source property. The source is a property of the image produced by the itemrenderer. I'd like to be able to do something that's the equivalent of
tileList.selectedItem.itemRenderer.source
But that doesn't do it either.
There's got to be a simple way to do this that I'm just missing. Any help would be much appreciated.
In your onMousePicDown handler your source should be:
dragView.source = event.target.parent.source;

Can an overlapping sibling prevent an Event?

Below is the code for a simple Flex actionscript project. A sprite is partially covering a hyperlink. What's happening is that when you hover over the sprite, if you're also hovering over the hyperlink, the hyperlink is activated. I want to prevent that. I want the hyperlink to be activated only when the mouse hovers over it -- but not when the mouse hovers over the sprite which covers it.
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TextEvent;
import flash.text.Font;
import flash.text.StyleSheet;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
public class SpriteHyperlinkTest extends Sprite
{
private var style : StyleSheet = new StyleSheet();
public function SpriteHyperlinkTest()
{
createOutputTextField();
}
public var output_txt : TextField;
private function createOutputTextField() : void {
// set styles
var hover : Object = new Object();
hover.fontWeight = "bold";
hover.color = "#0000FF";
var link : Object = new Object();
link.fontWeight = "bold";
link.textDecoration = "underline";
link.color = "#555555";
var active : Object = new Object();
active.fontWeight = "bold";
active.color = "#FF0000";
var visited : Object = new Object();
visited.fontWeight = "bold";
visited.color = "#cc0099";
visited.textDecoration = "underline";
style.setStyle("a:link", link);
style.setStyle("a:hover", hover);
style.setStyle("a:active", active);
style.setStyle(".visited", visited);
output_txt = new TextField();
output_txt.backgroundColor = 0xFFFFFF;
output_txt.background = true;
//output_txt.embedFonts = true;
output_txt.wordWrap = true;
output_txt.multiline = true;
output_txt.name = "output_txt";
output_txt.x = 100;
output_txt.y = 100;
output_txt.width = 300;
output_txt.height = 200;
output_txt.htmlText = "<b>sample <a href='http://www.google.com'>hyperlink text</a></b>";
addChild(output_txt);
var mySprite:Sprite = new Sprite();
mySprite.graphics.lineStyle(.5,0x000000);
mySprite.graphics.beginFill(0xff0000, 1);
mySprite.alpha = .7;
mySprite.graphics.drawRect(100, 100, 90, 20);
mySprite.graphics.endFill();
mySprite.useHandCursor = true;
mySprite.mouseChildren = true;
mySprite.buttonMode = true;
mySprite.name = "Sprite1";
this.addChild(mySprite);
output_txt.styleSheet = style;
}
}
}
I've almost found a workaround. It is just amazing that there isn't some object that you can place on top of the textfield in order to prevent its seeing the mouse overing over a hyperlink.
Here, for the record, are some of the things I've tried.
stopPropagation () // didn't work
stopImmediatePropagation(P //didn't work
Because the textfield and the sprite are siblings on the displaylist, I think all of the event propagation stuff is irrelevant. All of that has to do with ancestors and descendants, but what we're dealing with here is neither.
I tried placing another ('cover') textfield on top of the 'target' textfield. I tried setting the cover.visible = false; but that just meant it effectively wasn't there. I tried setting its alpha to .1, but that didn't work either -- just like the covering sprite, it allowed the mouseOver to go through it, so that the hyperlink responded.
I thought about trying to use preventDefault () on the hyperlink but a) I don't know how to reference the hyperlink (it has no ID) and b) the only event that is dispatched from a hyperlink is the TextEvent, and that's when it's clicked. We're not clicking, we're hovering. So I don't know what event to cancel.
The other thing I thought to do was to a kind of a fake 'cancel'. That is, maybe I could set the textformat, or style, of the hyperlink to look like normal text while the mouse is hovering over the sprite. The hyperlink would actually be being activated, but it would look like it isn't being activated because the style would be changed. This is what worked.
But it's only a fake visual workaround...
I'd add a rollOver (or whatever event you're trying to block) event listener to the sprite on top of the hyperlink. In the rollover handler, be sure to call event.stopImmediatePropagation() and that should prevent the hyperlink underneath from receiving the event.
I've never seen that sort of behavior before. Your sample code doesn't even assign events. I would be curious to see how you are assigning the events, because that in itself could be the cause of your problem.

How do I implement custom drag functionality in a Flex list control?

Flex has built in drag-n-drop for list controls, and allows you to override this. But they don't cover this in examples. The built-in functionality automatically drags the list-item, if you want to override this you find the handlers are being set up on the list itself.
What I specifically want to do, is my TileList shows small thumbnails of items I can drag onto a large Canvas. As I drag an item from the list, the drag proxy should be a different image.
So, I followed the technique suggested and it only works if I explicitly set the width/height on the proxy Image. Why?
It's not obvious until you've tried it =) I struggled with the same thing just a few weeks ago. This was my solution:
The list:
<List>
<mouseDown>onListMouseDown(event)</mouseDown>
</Tree>
The mouse down handler:
private function onMouseDown( event : MouseEvent ) : void {
var list : List = List(event.currentTarget);
// the data of the clicked row, change the name of the class to your own
var item : MyDataType = MyDataType(list.selectedItem);
var source : DragSource = new DragSource();
// MyAwsomeDragFormat is the key that you will retrieve the data by in the
// component that handles the drop
source.addData(item, "MyAwsomeDragFormat");
// this is the component that will be shown as the drag proxy image
var dragView : UIComponent = new Image();
// set the source of the image to a bigger version here
dragView.source = getABiggerImage(item);
// get hold of the renderer of the clicked row, to use as the drag initiator
var rowRenderer : UIComponent = UIComponent(list.indexToItemRenderer(list.selectedIndex));
DragManager.doDrag(
rowRenderer,
source,
event,
dragView
);
}
That will start the drag when the user clicks an item in the list. Notice that I don't set dragEnabled and the other drag-related properties on the list since I handle all that myself.
It can be useful to add this to the beginning of the event handler:
if ( event.target is ScrollThumb || event.target is Button ) {
return;
}
Just to short circuit if the user clicks somewhere in the scrollbar. It's not very elegant but it does the job.
I found a simpler answer here. That example extends a DataGrid control, but you can do the same with a List control. In my case, I use an image source instead of Class:
public class CustomDragList extends List {
[Bindable]
public var dragProxyImageSource:Object;
override protected function get dragImage():IUIComponent {
var image:Image = new Image();
image.width = 50;
image.height = 50;
image.source = dragProxyImageSource;
image.owner = this;
return image;
}
}
Then use that custom list like this:
<control:CustomDragList
allowMultipleSelection="true"
dragEnabled="true"
dragProxyImageSource="{someImageSource}"
dragStart="onDragStart(event)"/>
Where 'someImageSource' can be anything you'd normally use for an image source (embedded, linked, etc.)

Resources