Processing...
Description & Source Code

ZK allows developers to compose existing components to form a new, reusable component. The dual listbox demonstrates how to extend the state and behavior of an existing component class in Java, while compose its UI presentation using a ZUML template.

dual_listbox.zul
<!-- Define Component Class -->
<?component name="dual-listbox" extends="div" class="demo.listbox.dual_listbox.DualListbox"?>

<window apply="demo.listbox.dual_listbox.DualComposer">
	<!-- Direct Use -->
	<dual-listbox id="dualLBox" />
</window>
v_dualListbox.zul
<!-- View of customized component DualListbox -->
<hlayout height="320px" style="padding:10px">
	<listbox id="candidateLb" hflex="1" vflex="true" multiple="true">
		<listhead>
			<listheader label="First Name" width="80px"></listheader>
			<listheader label="Last Name" width="80px"></listheader>
			<listheader label="Full Name"></listheader>
			<listheader label="Married" width="60px"></listheader>
		</listhead>
		<template name="model">
			<listitem>
				<listcell label="${each.firstName }"/>
				<listcell label="${each.lastName }"/>
				<listcell label="${each.fullName }"/>
				<listcell label="${each.married ? 'Y' : 'N' }"/>
			</listitem>
		</template>
	</listbox>
	<zscript><![CDATA[
	String imgPath = "/widgets/listbox/dual_listbox/img";
]]></zscript>
	<vlayout spacing="10px" width="24px">
		<image style="cursor:pointer" id="chooseAllBtn" src="${imgPath}/rightrightarrow_g.png" />
		<image style="cursor:pointer" id="chooseBtn" src="${imgPath}/rightarrow_g.png" />
		<image style="cursor:pointer" id="removeBtn" src="${imgPath}/leftarrow_g.png" />
		<image style="cursor:pointer" id="removeAllBtn" src="${imgPath}/leftleftarrow_g.png" />
	</vlayout>
	<listbox id="chosenLb" hflex="1" vflex="true" multiple="true">
		<listhead>
			<listheader label="First Name" width="80px"></listheader>
			<listheader label="Last Name" width="80px"></listheader>
			<listheader label="Full Name"></listheader>
			<listheader label="Married" width="60px"></listheader>
		</listhead>
		<template name="model">
			<listitem>
				<listcell label="${each.firstName }"/>
				<listcell label="${each.lastName }"/>
				<listcell label="${each.fullName }"/>
				<listcell label="${each.married ? 'Y' : 'N' }"/>
			</listitem>
		</template>
	</listbox>

	<vlayout spacing="10px" width="24px">
		<image style="cursor:pointer" id="topBtn" src="${imgPath}/upuparrow_g.png" />
		<image style="cursor:pointer" id="upBtn" src="${imgPath}/uparrow_g.png" />
		<image style="cursor:pointer" id="downBtn" src="${imgPath}/downarrow_g.png" />
		<image style="cursor:pointer" id="bottomBtn" src="${imgPath}/downdownarrow_g.png" />
	</vlayout>
</hlayout>
DualComposer.java
package demo.listbox.dual_listbox;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zul.ListModelList;

import demo.data.PersonData;
import demo.data.pojo.Person;

public class DualComposer extends SelectorComposer<Component> {
	private static final long serialVersionUID = 8243942703081449079L;
	
	@Wire
	private DualListbox dualLBox;

	// Handling dualListBox just like origional ZK component
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		dualLBox.setModel(PersonData.getAll());
	}
}
DualListbox.java
package demo.listbox.dual_listbox;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.IdSpace;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.select.Selectors;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zul.Div;
import org.zkoss.zul.ListModelList;
import org.zkoss.zul.Listbox;

import demo.data.pojo.Person;

public class DualListbox extends Div implements IdSpace {
	private static final long serialVersionUID = 5183321186606483396L;
	
	@Wire
	private Listbox candidateLb;
	@Wire
	private Listbox chosenLb;

	private ListModelList<Person> candidateModel;
	private ListModelList<Person> chosenDataModel;

	public DualListbox() {
		Executions.createComponents("/widgets/listbox/dual_listbox/v_dualListbox.zul", this, null);
		Selectors.wireComponents(this, this, false);
		Selectors.wireEventListeners(this, this);
		chosenLb.setModel(chosenDataModel = new ListModelList<Person>());
		chosenDataModel.setMultiple(true);
	}

	@Listen("onClick = #chooseBtn")
	public void chooseItem() {
		Events.postEvent(new ChooseEvent(this, chooseOne()));
	}

	@Listen("onClick = #removeBtn")
	public void unchooseItem() {
		Events.postEvent(new ChooseEvent(this, unchooseOne()));
	}

	@Listen("onClick = #chooseAllBtn")
	public void chooseAllItem() {
		chosenDataModel.addAll(candidateModel);
		candidateModel.clear();
	}

	@Listen("onClick = #removeAllBtn")
	public void unchooseAll() {
		candidateModel.addAll(chosenDataModel);
		chosenDataModel.clear();
	}

	@Listen("onClick = #topBtn")
	public void top() {
		Set<Person> selection = new LinkedHashSet<Person>(chosenDataModel.getSelection());
		chosenDataModel.removeAll(selection);
		chosenDataModel.addAll(0, selection);
		chosenDataModel.setSelection(selection);
	}

	@Listen("onClick = #upBtn")
	public void up() {
		Set<Person> selection = new LinkedHashSet<Person>(chosenDataModel.getSelection());
		if (selection.isEmpty())
			return;
		int index = chosenDataModel.indexOf(selection.iterator().next());
		if (index == 0 || index < 0)
			return;
		chosenDataModel.removeAll(selection);
		chosenDataModel.addAll(--index, selection);
		chosenDataModel.setSelection(selection);

	}

	@Listen("onClick = #downBtn")
	public void down() {
		Set<Person> selection = new LinkedHashSet<Person>(chosenDataModel.getSelection());
		if (selection.isEmpty())
			return;
		int index = chosenDataModel.indexOf(selection.iterator().next());
		if (index == chosenDataModel.size() - selection.size() || index < 0)
			return;
		chosenDataModel.removeAll(selection);
		chosenDataModel.addAll(++index, selection);
		chosenDataModel.setSelection(selection);
	}

	@Listen("onClick = #bottomBtn")
	public void bottom() {
		Set<Person> selection = new LinkedHashSet<Person>(chosenDataModel.getSelection());
		chosenDataModel.removeAll(selection);
		chosenDataModel.addAll(selection);
		chosenDataModel.setSelection(selection);
	}

	/**
	 * Set new candidate ListModelList.
	 * 
	 * @param candidate
	 *            is the data of candidate list model
	 */
	public void setModel(List<Person> candidate) {
		candidateLb.setModel(this.candidateModel = new ListModelList<Person>(candidate));
		this.candidateModel.setMultiple(true);
		chosenDataModel.clear();
	}

	/**
	 * @return current chosen data list
	 */
	public List<Person> getChosenDataList() {
		return new ArrayList<Person>(chosenDataModel);
	}

	private Set<Person> chooseOne() {
		Set<Person> set = candidateModel.getSelection();
		chosenDataModel.addAll(set);
		candidateModel.removeAll(set);
		return set;
	}

	private Set<Person> unchooseOne() {
		Set<Person> set = chosenDataModel.getSelection();
		candidateModel.addAll(set);
		chosenDataModel.removeAll(set);
		return set;
	}

	// Customized Event
	public class ChooseEvent extends Event {
		private static final long serialVersionUID = -7334906383953342976L;

		public ChooseEvent(Component target, Set<Person> data) {
			super("onChoose", target, data);
		}
	}
}
PersonData.java
package demo.data;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import demo.data.pojo.Person;

public class PersonData {

	public static final Map<Integer, Person> DUMMY_PERSON_TABLE = new LinkedHashMap<Integer, Person>();

	private static volatile int uuid = 0;

	static {
		Person person = null;

		person = new Person(++uuid, "Jabir", "Uilleag", false);
		DUMMY_PERSON_TABLE.put(person.getId(), person);

		person = new Person(++uuid, "Finbar", "Eetu", true);
		DUMMY_PERSON_TABLE.put(person.getId(), person);

		person = new Person(++uuid, "Brice", "Cainan", true);
		DUMMY_PERSON_TABLE.put(person.getId(), person);

		person = new Person(++uuid, "Issy", "Willy", true);
		DUMMY_PERSON_TABLE.put(person.getId(), person);

		person = new Person(++uuid, "Hugo ", "Wayne", false);
		DUMMY_PERSON_TABLE.put(person.getId(), person);
		
		person = new Person(++uuid, "Chelle", "Dinah", true);
		DUMMY_PERSON_TABLE.put(person.getId(), person);
		
		person = new Person(++uuid, "Lonnie", "Tarbox", true);
		DUMMY_PERSON_TABLE.put(person.getId(), person);
		
		person = new Person(++uuid, "Loraine", "Vassel", false);
		DUMMY_PERSON_TABLE.put(person.getId(), person);

	}

	public static List<Person> getAll() {
		return new ArrayList<Person>(DUMMY_PERSON_TABLE.values());
	}
	

}
Person.java
package demo.data.pojo;

public class Person {
	private int id;
	private String firstName = "";
	private String lastName = "";
	private boolean married = false;

	public Person() {
	}

	public Person(Integer id, String firstName, String lastName) {
		this.id = id;
		this.firstName = firstName;
		this.lastName = lastName;
	}

	public Person(Integer id, String firstName, String lastName, boolean married) {
		this.id = id;
		this.firstName = firstName;
		this.lastName = lastName;
		this.married = married;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public boolean isMarried() {
		return married;
	}

	public void setMarried(boolean married) {
		this.married = married;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public String getLastName() {
		return lastName;
	}

	public String getFullName() {
		return firstName + " " + lastName;
	}
}