Question

How can I get my actual object out of the Dragboard with getContent()? Is there any possibility to specify the type? Todo and Doing are FlowPanes - MyRectangle is an exammple of a custom component.

What I want to have: Put an Object eg. an Rectangle with height, filled color etc. on the clipboard and get that object back from the board with height, color etc....

private static final DataFormat itemFormat = new DataFormat("custom.item");
MyRectangle myRectangle = generateRectangle();

myRectangle.setOnDragDetected(new EventHandler<MouseEvent>() {

    @Override
    public void handle(MouseEvent event) {
        Dragboard db = myRectangle
            .startDragAndDrop(TransferMode.MOVE);

        ClipboardContent content = new ClipboardContent();
        content.put(taskFormat, myRectangle);

        // Rectangle has height 
        System.out.println(myRectangle.getHeight());
        TaskItem task = new TaskItem();
        task.setTime(6);
        content.put(itemFormat, task);
        db.setContent(content);

        event.consume();
    }
    });

    myRectangle.setOnDragDone(new EventHandler<DragEvent>() {
    public void handle(DragEvent event) {
        event.consume();
    }
    });

doing.setOnDragOver(new EventHandler<DragEvent>() {
    public void handle(DragEvent event) {
    if (event.getGestureSource() != doing) {
        event.acceptTransferModes(TransferMode.MOVE);
    }

    event.consume();
    }
});

doing.setOnDragEntered(new EventHandler<DragEvent>() {
    public void handle(DragEvent event) {
    event.consume();
    }
});

doing.setOnDragExited(new EventHandler<DragEvent>() {
    public void handle(DragEvent event) {
    event.consume();
    }
});

doing.setOnDragDropped(new EventHandler<DragEvent>() {
    public void handle(DragEvent event) {
    final Dragboard db = event.getDragboard();
    boolean success = false;
    if (db.hasContent(taskFormat)) {
        MyRectangle rect2 = (MyRectangle) db.getContent(taskFormat);


        System.out.println(rect2.getHeight());
        todo.getChildren().remove(rect2);
        doing.getChildren().add(rect2);
        success = true;
        // doing.getChildren().add(rectangle);
    }
    event.setDropCompleted(success);
    event.consume();
    }
});

private MyRectangle generateRectangle() {
final MyRectangle rect2 = new MyRectangle(0, 0, 10, 10);
rect2.setId("app");
rect2.setArcHeight(8);
rect2.setWidth(80);
rect2.setArcWidth(8);
rect2.setStrokeWidth(1);
rect2.setStroke(Color.WHITE);
rect2.setHeight(60);
return rect2;
}
Was it helpful?

Solution

Well, you really should be thinking about having a representation of the data (not the view of the data) which is the object that is dragged and dropped. You register a handler to detect the drag with a node (view of the data), set the data into the dragboard, and then on a successful drop create another view of the same data where you dropped it. Remove the previous Node for a move, don't remove it for a copy.

Unfortunately, it doesn't work.

See this and vote for this bug/feature, which would allow you to do this directly.

Currently you can only place Java objects on the drag board if they implement Serializable. Since the JavaFX Properties do not implement Serializable, any class that uses those for data representation (which, imho, is any class you'd want to use to represent data you want to drag and drop around an application). And even if your class is Serializable, as I understand it, the object is serialized on placing it in the dragboard and deserialized when you remove it, which means you'd get a new object when you dragged and dropped, not a reference to the same object: that's probably not what you want. (If you copy something by drag and drop, then edit it, you probably want both copies to respect the edit.)

So, for now, I think the solution is to create a local repository of some kind and store the dragged object in it. This might be as simple as just an ObjectProperty<?> currentlyDraggedObject, or something more complex like the LocalDragboard I implemented at the bottom of the discussion referenced earlier. (This is nothing more than copying the code you'll find if you google "standard example of a typesafe heterogeneous container".)

I have to say, I find the way drag and drop is done a bit weird. Almost everything in JavaFX 2 and later was written in a very modern style of Java, with (almost) everything using generics very comfortably, some very nice concurrency APIs that were designed for the newer high-level concurrency APIs, all the event handling designed with an eye to more recent language developments such as lambda expressions and streams. The Bindings API even seems to tip its hat slightly towards the whole reactive programming movement. But drag and drop seems to have been designed as though the only data we would ever want to transfer by drag gestures were Strings, Images, and Files. It's as though the designers of the DnD API hadn't really got their heads around the idea that programmers would want to, you know, develop their own data representation classes.

So in the midst of this very modern-looking GUI framework, you have a DnD API that looks like it was designed in the late 90s (if that). Very strange.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top