(Repaint?) problems with drag and drop between TreeTables

Hi, I am having some problems with drag and drop between TreeTables. I got a SplitPanel, on one side I alter between two TreeTables using some buttons to set the content. On the right side of the split i have a target TreeTable.
Run application:

  1. drag row from left to right treeTable:
    works
  2. push button 2 to change content of left panel:
    Tables are not drawn
  3. push same button again:
    Tables are drawn
  4. drag row from left to right treeTable:
    works
  5. push button 1 to change content of left panel:
    Tables are not drawn
  6. push button 2 :
    Tables are drawn, but drag and drop no longer works
  7. click on header/or expand a node of table in right panel:
    drag and drop works again

Is it something I am doing wrong or is this a bug?

I am using vaadin 6.7.2 and treetable 1.2.2 ( if I use com.vaadin.ui.TreeTable the header of the table on the right side will always be empty)

Structure of components:

Source code:


package com.example.dragdropproblem;


import com.vaadin.addon.treetable.TreeTable;
import com.vaadin.annotations.AutoGenerated;
import com.vaadin.data.Item;
import com.vaadin.event.Transferable;
import com.vaadin.event.dd.DragAndDropEvent;
import com.vaadin.event.dd.DropHandler;
import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
import com.vaadin.event.dd.acceptcriteria.And;
import com.vaadin.event.dd.acceptcriteria.ClientSideCriterion;
import com.vaadin.event.dd.acceptcriteria.SourceIs;
import com.vaadin.ui.AbstractSelect.AcceptItem;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.CustomComponent;
import com.vaadin.ui.HorizontalSplitPanel;
import com.vaadin.ui.Panel;
import com.vaadin.ui.Table;
import com.vaadin.ui.Table.TableDragMode;
import com.vaadin.ui.VerticalLayout;

public class DragAndDropProblem extends CustomComponent {
	private VerticalLayout mainLayout;
	private HorizontalSplitPanel horizontalSplitPanel_1;
	private Panel panel_2;
	private VerticalLayout verticalLayout_3;
	private TreeTable treeTableDrop;
	private Panel panel_1;
	private VerticalLayout verticalLayout_1;
	private Button button_2;
	private Button button_1;

	private TreeTable treeTab1;
	private TreeTable treeTab2;
	private int counter = 0;

	public DragAndDropProblem() {
		buildMainLayout();
		setCompositionRoot(mainLayout);

		treeTab1 = new TreeTable();
		treeTab2 = new TreeTable();

		initTreeTables(treeTab1);
		initTreeTables(treeTab2);
		initReceivingTreeTab();
		
		setContentPanel_1(treeTab1);

		button_1.addListener(new Button.ClickListener() {
			public void buttonClick(ClickEvent event) {
				setContentPanel_1(treeTab1);
			}
		});
		button_2.addListener(new Button.ClickListener() {			
			public void buttonClick(ClickEvent event) {
				setContentPanel_1(treeTab2);	
			}
		});
	}
	public void setContentPanel_1(TreeTable tree){
		panel_1.removeAllComponents();
		panel_1.addComponent(tree);
	}
	
	private void initTreeTables(TreeTable treeTab) {
		counter ++;
		treeTab.addContainerProperty("name", String.class, "");
		treeTab.addContainerProperty("type", String.class, "");
		treeTab.setImmediate(true);
		treeTab.setSizeFull();
		
		//Add dummy data
		String random = ""+Math.random()*20;
		addRowTreeTable(treeTab, new String[]{"name","type"}, new Object[]
{random,counter}, null, false);
		random = ""+Math.random()*20;
		addRowTreeTable(treeTab, new String[]{"name","type"}, new Object[]
{random,counter}, null, false);
		random = ""+Math.random()*20;
		addRowTreeTable(treeTab, new String[]{"name","type"}, new Object[]
{random,counter}, null, false);

		//adding empty dropHandler
		final ClientSideCriterion acceptCriterion =  new SourceIs(treeTableDrop);
		treeTab.setDragMode(TableDragMode.ROW);
		treeTab.setDropHandler(new DropHandler() {
			public AcceptCriterion getAcceptCriterion() {
				return new And(acceptCriterion, AcceptItem.ALL);
			}
			public void drop(DragAndDropEvent event) {
				System.out.println("Do nothing");	
			}
		});
	}
	
	private void initReceivingTreeTab() {
		treeTableDrop.setImmediate(true);
		treeTableDrop.addContainerProperty("name", String.class, "");
		treeTableDrop.addContainerProperty("type", String.class, "");
		//adding dropHandler
		final ClientSideCriterion acceptCriterion =  new SourceIs(treeTab1,treeTab2);
		treeTableDrop.setDragMode(TableDragMode.ROW);
		treeTableDrop.setDropHandler(new DropHandler() {
			public AcceptCriterion getAcceptCriterion() {
				return new And(acceptCriterion, AcceptItem.ALL);
			}
			public void drop(DragAndDropEvent event) {
				//the object that is dropped
				Transferable t = event.getTransferable();
				TreeTable source = (TreeTable) t.getSourceComponent();
				Object draggedId = t.getData("itemId");

				//adding item from source to treeTableDrop
				addRowTreeTable(treeTableDrop, source.getItem(draggedId), null, true);
			}
		});
	}

	public Object addRowTreeTable(TreeTable tree, Object[] pId, Object[]
 values, Object parentId, boolean allowChildren){
		//TODO: write check to see if pId exists in tree
		Object newRow = tree.addItem();
		for(int i=0;i<pId.length;i++){
			tree.getContainerProperty(newRow, pId[i]
).setValue(values[i]
);
		}
		tree.setChildrenAllowed(newRow, allowChildren);
		if(parentId!=null){
			tree.setChildrenAllowed(parentId, true);
			tree.setParent(newRow, parentId);
		}
		return newRow;
	}
	public Object addRowTreeTable(TreeTable tree, Item item, Object parentId, boolean allowChildren){
		Object[] pId = new Object[item.getItemPropertyIds().size()]
;
		Object[] values = new Object[item.getItemPropertyIds().size()]
;
		int indexCounter=0;
		for(Object propid : item.getItemPropertyIds()){
			pId[indexCounter]
 = propid;
			values[indexCounter]
 = item.getItemProperty(propid).getValue();
			indexCounter++;
		}
		return addRowTreeTable(tree, pId, values, parentId, allowChildren);
	}
	@AutoGenerated
	private VerticalLayout buildMainLayout() {
		// common part: create layout
		mainLayout = new VerticalLayout();
		mainLayout.setImmediate(false);
		mainLayout.setWidth("100%");
		mainLayout.setHeight("100%");
		mainLayout.setMargin(false);
		
		// top-level component properties
		setWidth("100.0%");
		setHeight("100.0%");
		
		// button_1
		button_1 = new Button();
		button_1.setCaption("Table 1");
		button_1.setImmediate(true);
		button_1.setWidth("-1px");
		button_1.setHeight("-1px");
		mainLayout.addComponent(button_1);
		
		// button_2
		button_2 = new Button();
		button_2.setCaption("Table 2");
		button_2.setImmediate(true);
		button_2.setWidth("-1px");
		button_2.setHeight("-1px");
		mainLayout.addComponent(button_2);
		
		// horizontalSplitPanel_1
		horizontalSplitPanel_1 = buildHorizontalSplitPanel_1();
		mainLayout.addComponent(horizontalSplitPanel_1);
		mainLayout.setExpandRatio(horizontalSplitPanel_1, 1.0f);
		
		return mainLayout;
	}
	@AutoGenerated
	private HorizontalSplitPanel buildHorizontalSplitPanel_1() {
		// common part: create layout
		horizontalSplitPanel_1 = new HorizontalSplitPanel();
		horizontalSplitPanel_1.setImmediate(false);
		horizontalSplitPanel_1.setWidth("100.0%");
		horizontalSplitPanel_1.setHeight("100.0%");
		horizontalSplitPanel_1.setMargin(false);
		
		// panel_1
		panel_1 = buildPanel_1();
		horizontalSplitPanel_1.addComponent(panel_1);
		
		// panel_2
		panel_2 = buildPanel_2();
		horizontalSplitPanel_1.addComponent(panel_2);
		
		return horizontalSplitPanel_1;
	}
	@AutoGenerated
	private Panel buildPanel_1() {
		// common part: create layout
		panel_1 = new Panel();
		panel_1.setImmediate(false);
		panel_1.setWidth("100.0%");
		panel_1.setHeight("100.0%");
		
		// verticalLayout_1
		verticalLayout_1 = new VerticalLayout();
		verticalLayout_1.setImmediate(false);
		verticalLayout_1.setWidth("100.0%");
		verticalLayout_1.setHeight("100.0%");
		verticalLayout_1.setMargin(false);
		panel_1.setContent(verticalLayout_1);
		
		return panel_1;
	}
	@AutoGenerated
	private Panel buildPanel_2() {
		// common part: create layout
		panel_2 = new Panel();
		panel_2.setImmediate(false);
		panel_2.setWidth("100.0%");
		panel_2.setHeight("100.0%");
		
		// verticalLayout_3
		verticalLayout_3 = buildVerticalLayout_3();
		panel_2.setContent(verticalLayout_3);
		
		return panel_2;
	}
	@AutoGenerated
	private VerticalLayout buildVerticalLayout_3() {
		// common part: create layout
		verticalLayout_3 = new VerticalLayout();
		verticalLayout_3.setImmediate(false);
		verticalLayout_3.setWidth("100.0%");
		verticalLayout_3.setHeight("100.0%");
		verticalLayout_3.setMargin(false);
		
		// treeTableDrop
		treeTableDrop = new TreeTable();
		treeTableDrop.setImmediate(false);
		treeTableDrop.setWidth("100.0%");
		treeTableDrop.setHeight("100.0%");
		verticalLayout_3.addComponent(treeTableDrop);
		
		return verticalLayout_3;
	}
}

Your source code is too long for me to try to debug it but I will try a guess in hope it helps.

In your code you do things like verticalLayout_3.addComponent(treeTableDrop);
At that moment the table seems to be still attached to the previous layout which is still being displayed.

So on your first click, the table is already “on screen” and does not let you reattach it → you get no table
Later in your code the layout using the table is being replaced → table get’s detached
On the second click, the table is detached, so it will attach itself to your new layout → you get a table

The drag and drop handlers may have also a problem with not being properly detached/reattached.

With Vaadin 6.7, you should use com.vaadin.ui.TreeTable instead of the add-on.
If the header is empty when it should not be, that is another problem… maybe you could investigate what might cause it, or try to create a minimal test application that demonstrates the problem.

Yeah it looks pretty long, but most of it is auto generated by the vaadin gui builder. I thougth to put it all in there so that it would be just to copy paste the class into a vaadin project, add an instance to the application main window, and run it.

But basically I have a HorizontalSplitPanel, I initialize three TreeTables with drop handlers, two of them with some dummy data and one empty, I put table1 in left split, and table3 in right split, and use two buttons to change the content of the left split between table1 and table2.

Case 1: Start : Dragging works fine until changing the content of the left split.
Case 2: Start : Changing the content works fine, but now the drop method is never called when dropping (until receiving table is repainted by refreshing the page/clicking the expand node/clicking the column header)

Sorry again for the confusion, but the layout stuff is auto generated, and I never change the layout after initialization, I only change the content of the panel (panel_1) in the left side of the HorizontalSplitPanel:

I change the content of the left split by


button_1.addListener(new Button.ClickListener() {
		public void buttonClick(ClickEvent event) {
			[b]
setContentPanel_1
[/b](treeTab1);
		}
	});
	button_2.addListener(new Button.ClickListener() {			
		public void buttonClick(ClickEvent event) {
			[b]
setContentPanel_1
[/b](treeTab2);	
		}
	});
public void [b]
setContentPanel_1
[/b](TreeTable tree){
	panel_1.removeAllComponents();
	panel_1.addComponent(tree);
}

The first problem is also present when using the com.vaadin.ui.TreeTable. And it is not so easy to make a much smaller test application when dealing with several treeTables with drag and drop, but apart from the auto generated stuff and the initialization of the tables there are not many lines left.

Anyway thank you for your replies:)

I have tried to make an as easy as possible example code to reproduce the problem, hope someone can help:


import com.vaadin.event.dd.DragAndDropEvent;
import com.vaadin.event.dd.DropHandler;
import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
import com.vaadin.event.dd.acceptcriteria.And;
import com.vaadin.event.dd.acceptcriteria.ClientSideCriterion;
import com.vaadin.event.dd.acceptcriteria.SourceIs;
import com.vaadin.ui.AbstractSelect.AcceptItem;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.CustomComponent;
import com.vaadin.ui.HorizontalSplitPanel;
import com.vaadin.ui.Table.TableDragMode;
import com.vaadin.ui.TreeTable;
import com.vaadin.ui.VerticalLayout;

public class DragAndDropProblem extends CustomComponent {

	private VerticalLayout mainLayout;
	private HorizontalSplitPanel horizontalSplitPanel_1;
	private TreeTable treeTableDrop;
	private Button button_2;
	private Button button_1;
	private TreeTable treeTab1;
	private TreeTable treeTab2;

	public DragAndDropProblem() {
		mainLayout = new VerticalLayout();
		mainLayout.setImmediate(true);
		
		// button_1
		button_1 = new Button("Table 1");
		button_1.setImmediate(true);
		mainLayout.addComponent(button_1);
		button_1.addListener(new Button.ClickListener() {
			public void buttonClick(ClickEvent event) {
				horizontalSplitPanel_1.setFirstComponent(treeTab1);
			}
		});
		
		// button_2
		button_2 = new Button("Table 2");
		button_2.setImmediate(true);
		mainLayout.addComponent(button_2);
		button_2.addListener(new Button.ClickListener() {			
			public void buttonClick(ClickEvent event) {
				horizontalSplitPanel_1.setFirstComponent(treeTab2);	
			}
		});
		
		// treeTable 1 and 2
		treeTab1 = new TreeTable();
		treeTab2 = new TreeTable();
		initTreeTables(treeTab1, "Table 1 ");
		initTreeTables(treeTab2, "Table 2 ");
				
		// treeTableDrop
		treeTableDrop = new TreeTable();
		treeTableDrop.setImmediate(true);
		treeTableDrop.setSizeFull();
		treeTableDrop.addContainerProperty("name", String.class, "");
				//Adding dropHandler
		final ClientSideCriterion acceptCriterion =  new SourceIs(treeTab1,treeTab2);
		treeTableDrop.setDragMode(TableDragMode.ROW);
		treeTableDrop.setDropHandler(new DropHandler() {
			public AcceptCriterion getAcceptCriterion() {
				return new And(acceptCriterion, AcceptItem.ALL);
			}
			public void drop(DragAndDropEvent event) {
				//adding an item to show that the listener kicked in
				Object newRow = treeTableDrop.addItem();
				treeTableDrop.getContainerProperty(newRow, "name").setValue("DROP!");
				System.out.println("DROP!");
			}
		});
		
		// horizontalSplitPanel_1
		horizontalSplitPanel_1 = new HorizontalSplitPanel();
		horizontalSplitPanel_1.setImmediate(true);
		horizontalSplitPanel_1.setWidth("300px");
		horizontalSplitPanel_1.setHeight("300px");
		horizontalSplitPanel_1.setFirstComponent(treeTab1);
		horizontalSplitPanel_1.setSecondComponent(treeTableDrop);

		mainLayout.addComponent(horizontalSplitPanel_1);
		mainLayout.setExpandRatio(horizontalSplitPanel_1, 1.0f);
		setCompositionRoot(mainLayout);
	}
	
	private void initTreeTables(TreeTable treeTab, String name) {
		treeTab.addContainerProperty("name", String.class, "");
		treeTab.setImmediate(true);
		treeTab.setSizeFull();
		
		//Add dummy data
		Object newRow = treeTab.addItem();
		treeTab.getContainerProperty(newRow, "name").setValue(name);

		//adding empty dropHandler
		treeTab.setDragMode(TableDragMode.ROW);
		treeTab.setDropHandler(new DropHandler() {
			public AcceptCriterion getAcceptCriterion() {
				return new And(AcceptItem.ALL);
			}
			public void drop(DragAndDropEvent event) {
				System.out.println("Do nothing");	
			}
		});
	}
}

I took a look at your code.

I can’t find it in the book but from all my tests handlers get cleared when a component gets detached.
So in your click listener you need to redo the setDropHandler() and your disappearing D&D problems will be gone.

For the table not being displayed, I looks to me like a bug, I think you should create a new ticket for it (do a quick search in case there is already one open).
I tried requestRepaintAll() on the components and the layouts containing it but it never shows correctly.

If you need a quick workaround :

  • as your first component in the split panel, put an horizontallayout with both treetables inside
  • give your treetables a 100% size
  • use “treeTab1.setVisible(true);treeTab2.setVisible(false);” in your click handlers

Like this you don’t detach/reattach tables but just show/hide them. The handlers should stay in working condition all the time and the user should not see any difference.

Thank you, your solution works fine.