# JFace Databinding



## lumo (14. Jul 2010)

hallo,

ich versuche gerade eine eigene komponente mit databinding support zu implementieren.
eigentlich ist das eine einfache komponente, die aus anderen SET/JFace komponenten besteht.

ich hab in einer testanwendung databinding schon verwendet, allerdings nur mit den fertigen viewer und textfeldern...

hier mal meine klasse, der gui componente


```
public class BasicComposite extends Composite {
	private Text text;

	/**
	 * Create the composite.
	 * 
	 * @param parent
	 * @param style
	 */
	public BasicComposite(Composite parent, int style) {
		super(parent, style);
		GridLayout gridLayout = new GridLayout(3, true);
		gridLayout.marginHeight = 0;
		gridLayout.marginWidth = 0;
		gridLayout.verticalSpacing = 0;
		setLayout(gridLayout);
		setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
		{
			Label label = new Label(this, SWT.NONE);
			label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false,
					false, 1, 1));
			label.setText("New Label");
		}
		{
			text = new Text(this, SWT.BORDER);
			text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false,
					2, 1));
		}

	}

	public DataBindingContext bind(Object obj, String fieldName) {
		// if the bindingContext is not valid... create a new one
		DataBindingContext bindingContext = new DataBindingContext();
		//
		IObservableValue textObserveTextObserveWidget = SWTObservables
				.observeText(text, SWT.Modify);
		IObservableValue objNameObserveValue = BeansObservables.observeValue(
				obj, fieldName);
		bindingContext.bindValue(textObserveTextObserveWidget,
				objNameObserveValue, null, null);
		//
		return bindingContext;
	}

	@Override
	protected void checkSubclass() {
		// Disable the check that prevents subclassing of SWT components
	}
}
```
und hier noch der test... wo man sieht, dass es nicht klappt...

```
public class FormTest {
	// private DataBindingContext m_bindingContext;

	protected Shell shell;
	private SampleObject obj;
	private BasicComposite bc;
	private BasicComposite bc2;

	/**
	 * Launch the application.
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		Display display = Display.getDefault();
		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
			public void run() {
				try {
					FormTest window = new FormTest();
					window.open();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Open the window.
	 */
	public void open() {
		Display display = Display.getDefault();
		createContents();
		shell.open();
		shell.layout();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}
	}

	/**
	 * Create contents of the window.
	 */
	protected void createContents() {
		shell = new Shell();
		shell.setSize(450, 300);
		shell.setText("SWT Application");
		shell.setLayout(new GridLayout(1, false));
		{
			bc = new BasicComposite(shell, SWT.NONE);
			bc.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1,
					1));
		}
		{
			bc2 = new BasicComposite(shell, SWT.NONE);
			bc2.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false,
					1, 1));
		}
		obj = new SampleObject();
		bc.bind(obj, "name");
		bc2.bind(obj, "name");
	}
}
```

eigentlich wollte ich nur, dass ich dann per bind nur noch das objekt mitgeben muss (und den databinding-context)

PS: bei 37°C oder mehr in diesem büro ists schwer nen kühlen kopf zu behalten :bahnhof:


----------



## Gast2 (15. Jul 2010)

Ich würde das Databinding nicht in die Klasse machen....
Ich würde eine getControl Methode machen und da dein text rausgeben und das Binding von draußen machen wie es üblich ist... =)


----------



## lumo (16. Jul 2010)

okay dann eben so 
danke für dein vorschlag! - nwerd ich gleich mal probieren


----------



## lumo (16. Jul 2010)

hab jetzt noch ein problem gefunden.
und zwar hab ich als beispiel ein databindign auf ein objekt obj.
dann wird das objekt durch eine neue instanz überlagert (also obj = new SampleObject())
nun klappt das databinding nicht mehr.

jetzt ist die frage... wie kann ich dem erklären dass das obj gleich dem alten obj ist?
oder wie kann ich das alte binding entfernen (gezielt das, aus meiner komponente) um es neu zu verbinden?


----------



## Gast2 (16. Jul 2010)

lumo hat gesagt.:


> hab jetzt noch ein problem gefunden.
> und zwar hab ich als beispiel ein databindign auf ein objekt obj.
> dann wird das objekt durch eine neue instanz überlagert (also obj = new SampleObject())
> nun klappt das databinding nicht mehr.
> ...



He was? 
Wenn du die Instanz änderst musst du natürlich das Binding nochmal ausführen...


----------



## lumo (16. Jul 2010)

klar, aber ich muss die alte instanz aus dem binding entfernen, sonst hab ich schlussendlich ein zugemülltes binding...

bsp

obj -> bind to text1
obj -> bind to text2

obj = new SampleObject();
obj -> bind to text1
obj -> bind to text2

wenn ich mir dann alle bindings ausgeben lasse, hab ich natürlich VIER, sollten aber nur zwei sein.. -> muss ich (bloss wie) die alten bindings (von text1 und text1) auflösen


----------



## Gast2 (16. Jul 2010)

also ich würde nichts auflösen... aber hab noch nicht soviel mit dem binding gemacht wildcard weiß bestimm mehr


----------



## lumo (16. Jul 2010)

so ich habs jetzt mit einem workaround gelöst (bin mir recht sicher dass das schöner geht)

ich hab in meinem plugin eine bindingfactory die die bindings für mich macht.
diese soll jetzt die bindings nicht nur erzeugen, sondern auch merken 

ergo, erstelt die Factory ein Product wird dieses Binding gespeichert in einem Product, welches sich merkt, auf welcher componente es hängt, und welches objekt (class) und welcher value (string/bezeichner) damit verbunden ist... so kann ich per .equals dann das richtige objekt raussuchen und disposen


```
public class BindingFactory {
	private static final boolean DEBUG = true;
	private static final BindingFactory instance = new BindingFactory();
	private static DataBindingContext bindingContext = null;

	private List<Product> products = new LinkedList<Product>();

	public void removeBinding(Bindable component, Object obj, String value) {
		Product removeMe = new Product(null, component, obj, value);
		for (int i = 0; i < products.size(); i++) {
			Product check = products.get(i);
			if (check.equals(removeMe)) {
				if (DEBUG) {
					System.out.println("Match found");
				}
				check.removeBinding();
				products.remove(i);
			}
		}
	}

	private static class Product {
		private Binding b = null;
		private Bindable component = null;
		private Class<?> obj = null;
		private String value = null;

		public Product(Binding b, Bindable component, Object obj, String value) {
			super();
			this.b = b;
			this.component = component;
			this.obj = obj.getClass();
			this.value = value;
		}

		public boolean equals(Product other) {
			if (component.equals(other.component) && obj.equals(other.obj)
					&& value.equals(other.value)) {
				return true;
			}
			return false;
		}

		public void removeBinding() {
			if (DEBUG) {
				System.out.println("removing binding from " + this);
			}
			b.dispose();
		}

		@Override
		public String toString() {
			return "Product [b=" + b + ", component=" + component + ", obj="
					+ obj + ", value=" + value + "]";
		}
	}

	public DataBindingContext getBindingContext() {
		if (bindingContext == null) {
			bindingContext = new DataBindingContext();
		}
		return bindingContext;
	}

	public static BindingFactory getInstance() {
		instance.getBindingContext();
		return instance;
	}

	/**
	 * UpdateValueStrategy string2ClassStrategy = new UpdateValueStrategy();<br>
	 * string2ClassStrategy.setConverter(new StringToDateConverter());<br>
	 * UpdateValueStrategy class2Stringstrategy = new UpdateValueStrategy();<br>
	 * class2Stringstrategy.setConverter(new DateToStringConverter());<br>
	 * <br>
	 * Sample:<br>
	 * BindingFactory.bind(bc1.getControl(), 300, obj, "date", new
	 * String2Date(), new Date2String());
	 */
	public DataBindingContext bind(Bindable bindable, Integer delay,
			Object obj, String fieldName, IConverter string2ClassConverter,
			IConverter class2StringConverter) {

		if (delay == null || delay < 0) {
			delay = 300;
		}
		UpdateValueStrategy string2ClassStrategy = null;
		UpdateValueStrategy class2Stringstrategy = null;

		if (string2ClassConverter != null & class2StringConverter != null) {
			string2ClassStrategy = new UpdateValueStrategy();
			string2ClassStrategy.setConverter(string2ClassConverter);
			class2Stringstrategy = new UpdateValueStrategy();
			class2Stringstrategy.setConverter(class2StringConverter);
		}

		IObservableValue textObserveTextObserveWidget = null;

		// text field should be bind - this will be the most used case
		if (bindable.getControl() instanceof Text) {
			textObserveTextObserveWidget = SWTObservables.observeDelayedValue(
					delay, SWTObservables.observeText(bindable.getControl(),
							SWT.Modify));
		} else {
			System.err.println(String.format(
					"This type (%s) is not supported by %s yet.", bindable
							.getControl().getClass().getSimpleName().getClass()
							.getSimpleName(), BindingFactory.class
							.getSimpleName()));
		}

		IObservableValue objNameObserveValue = BeansObservables.observeValue(
				obj, fieldName);

		// BIND THE VALUE
		Binding b = bindingContext
				.bindValue(textObserveTextObserveWidget, objNameObserveValue,
						string2ClassStrategy, class2Stringstrategy);
		Product p = new Product(b, bindable, obj, fieldName);
		products.add(p);
		if (DEBUG) {
			System.out.println("added +" + p);
		}
		return bindingContext;
	}
}
```


----------



## Gast2 (16. Jul 2010)

Sorry ich check dein Problem nicht mehr -.-...


----------



## lumo (16. Jul 2010)

also nochmal, ich versuchs einfacher zu erklären.
ich erstelle ein OBJ auf das wird aufgepasst.
wenn ich die werte im OBJ ändere bekommens alle mit.
wenn ich jetzt das OBJ austausche, gegen eine neue instanz (der variablenname bleibt der selbe)
geht auch der aufpasser verloren.
wenn ich einen neuen aufpasser drauf anstze hab ich einen aufpasser, der nix tut, ausser meinen speicher zuzumüllen. (im prinzip hat dann eine komponente - zb ein textfeld mehrere aufpasse, die aber alle auf meine variable OBJ aufpassen sollen - nur dass eben nur einer die aktuelle version hat)

darum musste ich mir die quell-informationen für das binding merken, sodass ich das binding aus meiner bindingliste (wird bei mir in den products gespeichet) wieder finde 

ich hoffe ich konnte jetzt etwas licht ins dunkel bringen und nicht den letzten funken verständnis auslöschen :toll:


----------



## Gast2 (16. Jul 2010)

Ich weiß nicht ob das ein Problem ist ich würde einfach eine methode bindValues() machen. Und diese erneut Aufrufen und das neue Objekt zu binden. Ich glaub nicht das man was aufräumen muss...

Eventuell reicht auch eine einfache Update Methode, warum willst du die Instanz auch ändern???
JFace Data Binding


----------

