RCP Jface binding 2D Arrays

Martyy

Mitglied
Hallo,

bisher habe ich das Databinding nie benutzt und habe mir mal mehrere Tutorials durchgelesen (Vogella etc.).

Die Daten (alles Strings) waren in 2D ArrayLists gespeichert, konkret: ArrayList<ArrayList<String>> aber das habe ich mal umgebaut zu ArrayList<Row>, wobei Row eine eigene Klasse ist die im Prinzip die innere ArrayList<String> speichert und getter und setter mit PropertyChangeSupport implementiert.

Zum Testen habe ich mal folgenden Code verwendet

Java:
		WritableList input = new WritableList(file.getLines(), Row.class);
		ViewerSupport.bind(this.tableViewer, input, 
		    BeanProperties.
		    values(new String[]{("bla")}));

Problem: der ganze content wird in einer spalte angezegt, erzeuge ich dynamisch viele standard-labels und setze sie entsprechend an die Stelle von "bla" ein, wird nichtsmehr angezeigt. Ein passender Label-Provider exstiert ausserdem

Java:
			column.setLabelProvider(new ColumnLabelProvider() {
				  int index;
			      @SuppressWarnings("unchecked")
				@Override
			      public String getText(Object element) {
			        
				        return ((Row) element).getRow().get(index-1); //-1 because of the enumeration-column at position 0.
			      }
			      @Override
			  		public void update(ViewerCell cell) {
				  		Object element = cell.getElement();
				  		index = cell.getColumnIndex();
				  		cell.setText(getText(element));
				  		Image image = getImage(element);
				  		cell.setImage(image);
				  		cell.setBackground(getBackground(element));
				  		cell.setForeground(getForeground(element));
				  		cell.setFont(getFont(element));
			  		}
			    });

wobei manuell noch die erste Spalte gesetzt wird mit einem eigenem Label-Provider um eine Zeilennummerierung mitzuführen.


Gibt es beim Jface Databinding schon eine eingebaute Möglichkeit Elemente aus String-Arrays als eigenständige Properties zu betrachten und jeweils automatisch eine eigene Spalte zu bekommen, am besten unter Verwendung des eigenen Label-Providers?

Vielen Dank
 
G

Gast2

Gast
Du speicherst rows nochmal in rows ab? Versteht dein Datenkonstrukt nicht. Warum hast du einer Liste in einer Liste?
 

Martyy

Mitglied
Also es gibt ein Array das die Row objekte enthält (interprätatorisch stehen die untereinander) und jedes Row-Object enthält Strings, für jede Spalte ein String. Der Inhalt kommt aus einer .csv-Datei und wurde "getokenized"

nochmal grafisch:

Row1: [Spalte1, Spalte2, Spalte3,....,Spalte n]
Row2: [Spalte1, Spalte2, Spalte3,....,Spalte n]
Row3: [Spalte1, Spalte2, Spalte3,....,Spalte n]

das ich die einzelnen Zeilen in CSVRow objekte gepackt habe hatte nur den Grund um PropertyChangeSupport-Methoden einzubauen.

Was ich nun möchte ist das jede Spalte vom Databinding selbstständig angelegt bzw. dass jedes Element in den Rows als Property erkannt wird. Ich habe keine Beispiele in Google dazu gefunden aber sowas muss doch gehen :/
 

Martyy

Mitglied
Im wesentlichem ist die Anzahl sogar statisch aber jede CSV-File kann unterschiedlich viele Spalten und Zeilen haben. Die Anzahl wäre somit schon recht einfach ermittelbar aber es gibt keine feste Zuordnung welche Spalte für was steht. Die Zuordnung legt der Benutzer fest, in dem er mit einem Kontextmenü (rechtsklick auf einen Spalten-header) einen von mehreren Typen wie z.B. Artikelnummer oder Beschreibung festlegt

KSKB folgt noch :)
 
G

Gast2

Gast
Das Problem ist der ViewerSupport.bind(this.tableViewer, input,
BeanProperties.
values(new String[]{("bla")}))

macht alles über Reflection und wenn du intern eine Liste verwaltest geht da nicht mehr, darum kannst du den Viewer Support nicht verwernden.
 

Martyy

Mitglied
vielen Dank für den Hinweis!

gibt es eine Möglichkeit das Ziel dennoch zu erreichen ohne selbst ein Databinding coden zu müssen ? auch wenn es jetzt nicht der ViewerSupport ist, sondern eventuell das manuelle Anlegen des Bindingcontexts etc.
 
Zuletzt bearbeitet:
G

Gast2

Gast
Ohne deine Datenstruktur zu kennen kann ich wenig sagen, wie gesagt KSKB wäre hilfreich

aber wenn deine Klasse Row nur intern eine Liste verwaltet wie soll das dann gehen? Du benötigst ProperyChange Event nicht beim adden/removen von rows sondern wenn sich der inhalt ändert...
 

Martyy

Mitglied
here we go...

Class CSVRow:
Java:
package de.inovel.merlin.client.reade.bom.csv;

import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.beans.PropertyChangeSupport;

public class CSVRow {

	//private ArrayList<String> row = new ArrayList<String>();
	private ArrayList<CSVEntry> row = new ArrayList<CSVEntry>();

	private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
	
	public CSVRow(){
		
	}
	
	public CSVRow(ArrayList<CSVEntry> row){
		this.setRow(row);
	}

	public CSVRow(CSVRow row){
		this.setRow(row.getRow());
	}
	
	public Object clone(){
		CSVRow newCSVRow = new CSVRow();
		//deep copy
		ArrayList<CSVEntry> newRow = new ArrayList<CSVEntry>();
		for(CSVEntry element: row){
			newRow.add(new CSVEntry(element));
		}
		newCSVRow.setRow(newRow);
		return newCSVRow;
	}
	
	public void setRow(CSVRow row){
		this.setRow(row.getRow());
	}
	
	public void setRow(ArrayList<CSVEntry> row){
		propertyChangeSupport.firePropertyChange("row", this.row, this.row = row);
	}
	
	public ArrayList<CSVEntry> getRow() {
		return new ArrayList<CSVEntry>(this.row);
	}
	
	public CSVEntry getElement(int columnIndex){
		return this.row.get(columnIndex);
	}
	
	public String getEntry(int columnIndex){
		return this.row.get(columnIndex).getEntry();
	}
	
	public int size(){
		return this.row.size();
	}
	
	public void addElement(CSVEntry entry){
		this.row.add(entry);
		ArrayList<CSVEntry> tmp = this.row;
		propertyChangeSupport.firePropertyChange("row", this.row, tmp);
	}
	
	public void addAll(ArrayList<CSVEntry> row){
		this.row.addAll(row);
		ArrayList<CSVEntry> tmp = this.row;
		propertyChangeSupport.firePropertyChange("row", this.row, tmp);
	}

	public void removeElement(int idx){
		this.row.remove(idx);
		ArrayList<CSVEntry> tmp = this.row;
		propertyChangeSupport.firePropertyChange("row", this.row, tmp);
	}
	
	public void setElement(int columnIdx, String value){
		//ArrayList<CSVEntry> tmpRow = row;
		this.row.get(columnIdx).setEntry(value);
		//propertyChangeSupport.firePropertyChange("row", this.row, this.row = tmpRow);
	}
	
	public void setElement(int columnIdx, CSVEntry entry){
		this.row.get(columnIdx).setEntry(entry.getEntry());
	}
	
	public void addPropertyChangeListener(PropertyChangeListener listener) {
		propertyChangeSupport.addPropertyChangeListener(listener);
	}

	public void removePropertyChangeListener(PropertyChangeListener listener) {
		propertyChangeSupport.removePropertyChangeListener(listener);
	}

	public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
		propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
	}

	public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
		propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
	}

	protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
		propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
	}

}


Class CSVEntry:

Java:
package de.inovel.merlin.client.reade.bom.csv;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

public class CSVEntry {

	private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
	private String entry;
	
	public CSVEntry(String entry){
		this.setEntry(entry);
	}
	
	public CSVEntry(CSVEntry entry){
		this.setEntry(entry.getEntry());
	}
	
	public void setEntry(String entry){
		propertyChangeSupport.firePropertyChange("entry", this.entry, this.entry = entry);
	}
	
	public String getEntry(){
		return this.entry;
	}
	
	public void addPropertyChangeListener(PropertyChangeListener listener) {
		propertyChangeSupport.addPropertyChangeListener(listener);
	}

	public void removePropertyChangeListener(PropertyChangeListener listener) {
		propertyChangeSupport.removePropertyChangeListener(listener);
	}

	public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
		propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
	}

	public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
		propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
	}

	protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
		propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
	}
}

Java:
package de.inovel.merlin.client.reader.bom.views;

import java.util.ArrayList;


public class BomImportImpl extends ViewPart implements ISaveablePart
{
	protected boolean dirty;
	protected TableViewer tableViewer;
	protected Composite parent;
	protected String viewName;
	
	public BomImportImpl()
	{
		// TODO Auto-generated constructor stub
	
	}

	@Override
	public void doSave(IProgressMonitor monitor)
	{
		resetDirty();
	}

	@Override
	public void doSaveAs()
	{
		// TODO Auto-generated method stub

	}

	@Override
	public boolean isDirty()
	{
		return dirty;
	}

	@Override
	public boolean isSaveAsAllowed()
	{
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isSaveOnCloseNeeded()
	{
		// TODO Auto-generated method stub
		return false;
	}
	@Override
	public void createPartControl(Composite parent)
	{
		this.parent = parent;
		final Display display = parent.getDisplay();
		
		
		ScrolledComposite scrolledComposite = new ScrolledComposite(parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
		scrolledComposite.setExpandHorizontal(true);
		scrolledComposite.setExpandVertical(true);
		
		Composite composite = new Composite(scrolledComposite, SWT.NONE);
		composite.setLayout(new GridLayout(1, false));

		tableViewer = new TableViewer(composite, SWT.BORDER | SWT.FULL_SELECTION);
		//tableViewer.setContentProvider(ArrayContentProvider.getInstance());
		tableViewer.setContentProvider(new ObservableListContentProvider());


	
		
		Table table = tableViewer.getTable();
		table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
		// Make lines and make header visible
		table.setHeaderVisible(true);
		table.setLinesVisible(true);

		scrolledComposite.setContent(composite);
		scrolledComposite.setMinSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
	
		
	
	@Override
	public void setFocus()
	{
		tableViewer.getControl().setFocus();
	}
	
	protected void resetDirty() {
		if (dirty) {
			dirty = false;
			firePropertyChange(IEditorPart.PROP_DIRTY);
		}
	}
	/**
	 * This method sets the dirty flag
	 */
	protected void setDirty()
	{
		if (!dirty)
		{
			dirty = true;
			UIJob uiJob = new UIJob("Update Ui")
			{
				@Override
				public IStatus runInUIThread(IProgressMonitor monitor)
				{
					firePropertyChange(PROP_DIRTY);
					return Status.OK_STATUS;
				}
			};
			uiJob.schedule();
		}
	}	
	//OK
	

				
	public void processInput(ArrayList<CSVRow> tabelle){
						

		//TODO
		//DataBinding

		IObservableList input = Properties.selfList(CSVRow.class).observe(file.getSeparatedLines());
		//WritableList input = new WritableList(list, CSVRow.class);
		input = new WritableList(file.getSeparatedLines(), CSVEntry.class);
		IValueProperty entriesProperty =  BeanProperties.value(CSVRow.class, "row");
		IObservableList personNamesList = entriesProperty.observeDetail(input);
		
		
		ViewerSupport.bind(this.tableViewer, input, BeanProperties.list("row").values(CSVEntry.class, new String[]{"entry"}));
		this.tableViewer.refresh();
		setDirty();
	}	
	
}

Main:

Java:
public static void main(String [ ] args)
{
     CSVEntry e11 = new CSVEntry("Dies");
     CSVEntry e12 = new CSVEntry("ist");
     CSVEntry e13 = new CSVEntry("die 1");
     CSVEntry e14 = new CSVEntry("Zeile");

     CSVEntry e21 = new CSVEntry("Und dies");
     CSVEntry e22 = new CSVEntry("ist");
     CSVEntry e23 = new CSVEntry("die 2");
     CSVEntry e24 = new CSVEntry("Zeile");

     ArrayList<CSVEntry> row1List = new ArrayList<CSVEntry>();
     row1List.add(e11); 
     row1List.add(e12);
     row1List.add(e13);
     row1List.add(e14);
     ArrayList<CSVEntry> row2List = new ArrayList<CSVEntry>();
     row2List.add(e21); 
     row2List.add(e22);
     row2List.add(e23);
     row2List.add(e24);

     CSVRow row1 = new CSVRow(row1List);
     CSVRow row2 = new CSVRow(row2List);

ArrayList<CSVRow> Zeilen = new ArrayList<CSVRow>();
Zeilen.add(row1); Zeilen.add(row2);

BomImportImpl viewer = new BomImportImpl();
viewer.processInput(Zeilen);


}


hab jetzt die innere String array nochmal auf ein inneres CSVEntry-Array geändert und rumgetestet aber scheint wohl so garnichtzu klappen. Es muss doch eine vernünftige möglichkeit geben das problem zu lösen :( ... also das die Properties in einem Array gehalten werden (nach dem die Tabelle angelegt wurde ändert sich nichts mehr an der länge, da die Tabelleneinträge aber aus einer csv-datei kommen, weiß an vorher nicht wiviele spalten man haben wird)
 
Zuletzt bearbeitet:
G

Gast2

Gast
Fehlt noch ein LabelProvider, ich verstehe auch nicht was genau dein Problem ist, was du durch das Databinding erreichen willst? Klappt nur die anzeige nicht? Oder wenn du ein Objekt änderst wird nix angezeigt?

Also hier werden deine Zeilen nun angezeigt... und hinzufügen klappt nach, was hier noch nicht klappt ist wenn du den Inhalt der CSVRow änderst... Da fehlt dann noch weiteres Databinding auf die CSVRow usw.


Java:
package test;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.databinding.observable.list.WritableList;
import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Table;
import org.eclipse.ui.part.ViewPart;

public class View extends ViewPart
    {
    public static final String ID = "test.view";

    protected boolean          dirty;
    protected TableViewer      tableViewer;
    protected Composite        parent;
    protected String           viewName;

    private WritableList       input;

    @Override
    public void createPartControl(Composite parent)
        {
        this.parent = parent;

        ScrolledComposite scrolledComposite = new ScrolledComposite(parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
        scrolledComposite.setExpandHorizontal(true);
        scrolledComposite.setExpandVertical(true);

        Composite composite = new Composite(scrolledComposite, SWT.NONE);
        composite.setLayout(new GridLayout(1, false));

        tableViewer = new TableViewer(composite, SWT.BORDER | SWT.FULL_SELECTION);

        ObservableListContentProvider cp = new ObservableListContentProvider();
        tableViewer.setContentProvider(cp);

        Table table = tableViewer.getTable();
        table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
        // Make lines and make header visible
        table.setHeaderVisible(true);
        table.setLinesVisible(true);

        scrolledComposite.setContent(composite);
        scrolledComposite.setMinSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT));

        CSVEntry e11 = new CSVEntry("Dies");
        CSVEntry e12 = new CSVEntry("ist");
        CSVEntry e13 = new CSVEntry("die 1");
        CSVEntry e14 = new CSVEntry("Zeile");

        CSVEntry e21 = new CSVEntry("Und dies");
        CSVEntry e22 = new CSVEntry("ist");
        CSVEntry e23 = new CSVEntry("die 2");
        CSVEntry e24 = new CSVEntry("Zeile");

        final ArrayList<CSVEntry> row1List = new ArrayList<CSVEntry>();
        row1List.add(e11);
        row1List.add(e12);
        row1List.add(e13);
        row1List.add(e14);

        ArrayList<CSVEntry> row2List = new ArrayList<CSVEntry>();
        row2List.add(e21);
        row2List.add(e22);
        row2List.add(e23);
        row2List.add(e24);

        final CSVRow row1 = new CSVRow(row1List);
        CSVRow row2 = new CSVRow(row2List);

        final ArrayList<CSVRow> Zeilen = new ArrayList<CSVRow>();
        Zeilen.add(row1);
        Zeilen.add(row2);

        int i = 0;

        for (CSVEntry csvEntry : row1List)
            {
            TableViewerColumn column = new TableViewerColumn(tableViewer, SWT.NONE);
            column.getColumn().setText("Test");
            column.getColumn().setWidth(200);
            final int index = i;
            column.setLabelProvider(new ColumnLabelProvider()
                {
                    @Override
                    public String getText(Object element)
                        {
                        System.out.println(index);
                        CSVRow csvRow = (CSVRow)element;
                        return csvRow.getEntry(index);
                        }
                }
                    );
            i++;
            }
        processInput(Zeilen);
        new Thread(new Runnable()
            {

                @Override
                public void run()
                    {
                    for (int j = 0; j < 100; j++)
                        {
                        try
                            {
                            Thread.sleep(1000);
                            Display.getDefault().syncExec(new Runnable()
                                {

                                    @Override
                                    public void run()
                                        {
                                        input.add(row1);
                                        }
                                });
                            }
                        catch (InterruptedException e)
                            {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                            }
                        }

                    }
            }).start();
        }

    @Override
    public void setFocus()
        {
        tableViewer.getControl().setFocus();
        }

    public void processInput(List<CSVRow> tabelle)
        {
        input = new WritableList(tabelle, CSVRow.class);
        tableViewer.setInput(input);
        }
    }
 
Zuletzt bearbeitet von einem Moderator:

Martyy

Mitglied
Vielen Dank erstma bis hierhin!

den LabelProvider habe ich nur vergessen zu kopieren, die Anzeige hatte ohne das Binding vorher schon korrekt funktioniert. Das Problem ist das Binding an sich aber eventuell kann ich die Spalten direkt an die elemente der einzelnen Positionen im Array binden?
 
G

Gast2

Gast
Wie gesagt ich kenn dein Ziel nicht, keine Ahnung was du vor hast, das Binding geht zum Hinzufügen und wegnehmen von elementen. Jetzt musst halt die inneren Klassen binden...

Kannst auch mal EMF anschauen, vielleicht bringt das dir das schon mit...
 

Martyy

Mitglied
das Ziel ist eigentlich nur (naja "nur") das Änderungen die am Datenmodell vorgenommen werden automatisch in die Tabelle übernommen werden. Die Änderungen passieren nicht durch Manipulation der Tabelle durch den Benutzer (also keine Cell-Editor Geschichten), sondern nur durch andere Klassen die die setter Klasse in CSVEntry aufrufen.

Ich lese gerade jedes Tutorial durch das ich finde aber scheinbar bin ich nicht kreativ genug einen Weg zu finden, da diese BeanProperty Methoden alle "Properties" (pro Spalte eines) erwarten. Aber hier liegt ja nicht so eine statische Konstruktion vor.

Eine Stückliste könnte z.b. so aussehen:

ID | Beschreibung | Kosten
35 | blablabla | 23
55 | asdasd | 11


aber auch so

ID | Beschreibung | Gewicht | Lieferbar
23 | dadada | 22 | ja
11 | asdf | 11 | nein


oder auch so

ID | nicht zugewiesen | nicht zugewiesen | Kosten | Beschreibung
-----


der Spalten "Typ" wird vom Benutzer festgelegt und den speichere ich in jeder eingelesnen Liste in einem extra Array und so ist das ganze recht flexibel aber angenommen ich habe jetzt die unterste Liste eingelesen (mit 2 Spalten die undefiniert "nicht zugewiesen" bleiben), möchte ich diese mit dem Databinding betreiben. Sollte dann ein Eintrag für eine Zelle bzw. CSVEntry im Datanmodell (diese geschachtelten CSVRow/Entry's) verändert werden, so soll die Änderung in der Tabelle übernommen werden
 

Martyy

Mitglied
Also ich habe ja nur ein Property "row" in CSVRow, das ist mein Problem :( aber statisch festlegen ist nicht wirklich möglich mit dem was ich vorhabe, da Spalten doppelt vorkommen dürfen
 
G

Gast2

Gast
Ich hab schon lang kein Tabelle selber mehr gebunden, wie gesagt ich benutze EMF. Wenn du es nicht hinbekommst kannst du es natürlich auch noch von Hand machen. ProeprtyChangeListener anhängen und auf die Änderung selber reagieren...

Aber die erste Liste hast du ja gebunden. Des musst du noch die andere Liste binden an einen Databinding context und dann mit BeanProperties deinen CSVEntry binden...
 

Ähnliche Java Themen


Oben