EMF - EditingDomain/CommandStack

overflow

Mitglied
Hallo mal wieder,

hab mich nach meinen letzten Fragen etwas mehr in die Materie eingelesen und auch schon einige Dinge auf die Reihe bekommen. Jetzt steh ich grad vor dem Problem, dass ich meine bisher direkten Bindings gerne dahingehend ändern würde, dass alles über einen CommandStack läuft, um z.B. eine Speichern/Verwerfen Funktionalität zu bekommen.

Hier mal eine meiner Klassen, sie stellt ein Composite zur Verfügung, in der eine meiner EMF-Klassen bearbeitet werden kann (Stammdaten). Hat den Hintergrund, dass dieses Composite dann je nach dem auf ner View oder einem Dialog oder wo auch immer platziert wird, und das übergebene Objekt bearbeiten soll.

Java:
public class EditStammdatenPanel extends EditDefault{

	private Text txtVorname;
	private Text txtNachname;
	private DateTime dtGeburtsdatum;
	private Text txtEmail;
	
	private Combo cmbStaatsbuergerschaft;
	private ComboViewer cvStaatsbuergerschaft;

	private Combo cmbAnrede;
	private ComboViewer cmbViewer;
	
	private ObservableListContentProvider obsListContentProvider;
	
	public EditStammdatenPanel(Composite _parent, Person _aktPerson, int _spaltenDesPanels) {
		super(_parent, _aktPerson);
		createPanel(_spaltenDesPanels);
	}
	
	public EditStammdatenPanel(Composite _parent, Person _aktPerson, int _spaltenDesPanels, EMFDataBindingContext _emfDataBindingContext) {
		this(_parent, _aktPerson, _spaltenDesPanels);
		emfDataBindingContext = _emfDataBindingContext;
	}
	
	private void createPanel(int spaltenAnzahl) {
		
		Label lblAnrede = new Label(parent, SWT.NONE);
		lblAnrede.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
		lblAnrede.setText("Anrede");
		
		cmbViewer = new ComboViewer(parent, SWT.READ_ONLY);
		cmbViewer.setContentProvider(new ArrayContentProvider());
		cmbViewer.setInput(Anrede.values());
		cmbAnrede = cmbViewer.getCombo();
		cmbAnrede.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
		if(spaltenAnzahl == 4) {
			new Label(parent, SWT.NONE);
			new Label(parent, SWT.NONE);
		}
		
		Label lblVorname = new Label(parent, SWT.NONE);
		lblVorname.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
		lblVorname.setText("Vorname");
		
		txtVorname = new Text(parent, SWT.BORDER);
		txtVorname.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
		
		Label lblNachname = new Label(parent, SWT.NONE);
		lblNachname.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
		lblNachname.setText("Nachname");
		
		txtNachname = new Text(parent, SWT.BORDER);
		txtNachname.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
		
		Label lblGeburtsdatum = new Label(parent, SWT.NONE);
		lblGeburtsdatum.setText("Geburtsdatum");
		
		dtGeburtsdatum = new DateTime(parent, SWT.BORDER | SWT.DROP_DOWN);
		GridData gd_dtGeburtsdatum = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
		gd_dtGeburtsdatum.widthHint = 118;
		dtGeburtsdatum.setLayoutData(gd_dtGeburtsdatum);
		
		Label lblStaatsbrgerschaft = new Label(parent, SWT.NONE);
		lblStaatsbrgerschaft.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
		lblStaatsbrgerschaft.setText("Staatsb\u00FCrgerschaft");
		
		cvStaatsbuergerschaft = new ComboViewer(parent, SWT.READ_ONLY);
		obsListContentProvider = new ObservableListContentProvider();
		cvStaatsbuergerschaft.setContentProvider(obsListContentProvider);
		cmbStaatsbuergerschaft = cvStaatsbuergerschaft.getCombo();
		cmbStaatsbuergerschaft.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
		
		Label lblEmail = new Label(parent, SWT.NONE);
		lblEmail.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
		lblEmail.setText("Email");
		
		txtEmail = new Text(parent, SWT.BORDER);
		txtEmail.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
		new Label(parent, SWT.NONE);
		new Label(parent, SWT.NONE);
		
	}
	
	@Override
	protected void initDataBindings() {

		if(emfDataBindingContext == null)
			emfDataBindingContext = new EMFDataBindingContext();
		
		IValueProperty valProp = ViewerProperties.singleSelection();
		IEMFValueProperty emfValProp = EMFProperties.value(MitgliederPackage.Literals.PERSON__ANREDE);
		emfDataBindingContext.bindValue(valProp.observe(cmbViewer), emfValProp.observe(aktPerson));
		//
		IObservableValue swtObsVal = SWTObservables.observeText(txtVorname, SWT.Modify);
		IObservableValue emfObsVal = EMFObservables.observeValue(aktPerson, Literals.PERSON__VORNAME);
		System.out.println(aktPerson.toString());
		emfDataBindingContext.bindValue(swtObsVal, emfObsVal, null, null);
		//
		swtObsVal = SWTObservables.observeText(txtNachname, SWT.Modify);
		emfObsVal = EMFObservables.observeValue(aktPerson, Literals.PERSON__NACHNAME);
		emfDataBindingContext.bindValue(swtObsVal, emfObsVal, null, null);
		//
		swtObsVal = SWTObservables.observeText(txtEmail, SWT.Modify);
		emfObsVal = EMFObservables.observeValue(aktPerson, Literals.PERSON__EMAIL);
		emfDataBindingContext.bindValue(swtObsVal, emfObsVal, null, null);
		//
		//
		swtObsVal = SWTObservables.observeSelection(dtGeburtsdatum);
		emfValProp = EMFProperties.value(MitgliederPackage.Literals.PERSON__GEBURTSDATUM);
		emfDataBindingContext.bindValue(swtObsVal, emfValProp.observe(aktPerson));
		//
		//
		IObservableMap obsMap = EMFProperties.value(Literals.LAND__NAME).observeDetail(obsListContentProvider.getKnownElements());
		cvStaatsbuergerschaft.setLabelProvider(new ObservableMapLabelProvider(obsMap));
		IEMFListProperty listProp = EMFProperties.list(Literals.LAENDER__LIST_LAENDER);
		cvStaatsbuergerschaft.setInput(listProp.observe(laender));
		emfValProp = EMFProperties.value(Literals.PERSON__STAATSBUERGERSCHAFT);
		valProp = ViewerProperties.singleSelection();
		emfDataBindingContext.bindValue(valProp.observe(cvStaatsbuergerschaft),emfValProp.observe(aktPerson));
	}

	@Override
	public void doBinding(Person _Person, EMFDataBindingContext _emfDataBindingContext) {
		aktPerson = _Person;
		
		if(emfDataBindingContext != null)
			emfDataBindingContext.dispose();
		
		emfDataBindingContext = _emfDataBindingContext;
		initDataBindings();
	}
}
Java:
public abstract class EditDefault {

	protected Composite parent;
	
	protected Person aktPerson;
	protected Laender laender;
	
	protected EMFDataBindingContext emfDataBindingContext;
	
	public EditDefault(Composite _parent, Person _aktPerson) {
		parent = _parent;
		aktPerson = _aktPerson;
		laender = ModelProvider.INSTANCE.getLaender();
	}
	
	protected abstract void initDataBindings();
	public abstract void doBinding(Person _Person, EMFDataBindingContext _emfDataBindingContext);
	
	public void setParent(Composite _parent) {
		parent = _parent;
	}
	
	public Composite getParent() {
		return parent;
	}
	
	public void setEMFDataBindingContext(EMFDataBindingContext _emfDBC) {
		emfDataBindingContext = _emfDBC;
	}
	
	public EMFDataBindingContext getEMFDataBindingContext() {
		return emfDataBindingContext;
	}
	
	public void setPerson(Person p) {
		aktPerson = p;
	}
	
	public Person getPerson() {
		return aktPerson;
	}
}

Fragen dazu:
- Gibt es wo eine verständlichere Anleitung als das, was auf Tom Schindls Blog ist? Das war mir etwas zu weit verzahnt, als dass ich es problemlos verstanden hätte...
Ein simples Beispiel mit Person Vorname/Nachname und einer View, wo beides mittels "Speichern" verarbeitet wird, würde auch voll reichen... nur das was ich bisher gesehn hab, war mir zu komplex :(
- Ist der restliche Ansatz mit dem Databinding richtig, oder mache ich hier auch irgendwo Fehler? Funktionieren tut alles wie gewünscht, aber ich weiss nicht, ob ich nicht eventuell daten irgendwie am Modell vorbeischmuggle oder so...

Zusatzfrage:
Ich hab in meinem Modell eine Länder Klasse, welche eine Liste aus einzelnen Land objekten hält. Diese benutze ich, um alle möglichen Links herzustellen wie Staatsbürgerschaft, Telefonvorwahlen, etc. Um auf diese Länder-Liste zugreifen zu können, habe ich sie in einen kleinen Model-Provvider ausgelagert... Ich weiss nur nicht, ob das Sinnvoll ist, oder ob ich auch ohne diesen Umweg irgendwie direkt auf die Daten es EMF-Modells zugreifen könnte...

Java:
public enum ModelProvider {
	INSTANCE;
	
	private Mitglieder mitglieder;
	private Laender laender;
	final SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
	
	private ModelProvider() {
		mitglieder = MitgliederFactory.eINSTANCE.createMitglieder();
		
		laender = MitgliederFactory.eINSTANCE.createLaender();
		
		Land at = MitgliederFactory.eINSTANCE.createLand();
		at.setFahne("at");
		at.setName("Österreich");
		at.setVorwahl("+43");
		laender.getListLaender().add(at);
		
		Land de = MitgliederFactory.eINSTANCE.createLand();
		de.setFahne("de");
		de.setName("Deutschland");
		de.setVorwahl("+49");
		laender.getListLaender().add(de);
		
		Adresse adr = MitgliederFactory.eINSTANCE.createAdresse();
		adr.setAdressart(Adressarten.HAUPTWOHNSITZ);
		adr.setStrasse("111");
		adr.setPlz("222");
		adr.setOrt("333");
		adr.setLand(laender.getListLaender().get(0));
		
		Adresse adr2 = MitgliederFactory.eINSTANCE.createAdresse();
		adr2.setAdressart(Adressarten.ZWEITWOHNSITZ);
		adr2.setStrasse("STRASSE");
		adr2.setPlz("PLZ");
		adr2.setOrt("ORT");
		adr2.setLand(laender.getListLaender().get(1));
		
		Telefon t = MitgliederFactory.eINSTANCE.createTelefon();
		t.setArt(Telefonarten.MOBIL);
		t.setNummer("123123");
		t.setVorwahl(laender.getListLaender().get(0));
				
		Mitglied m = MitgliederFactory.eINSTANCE.createMitglied();
		m.setAnrede(Anrede.HERR);
		m.setVorname("1");
		m.setNachname("2");
		try {
			m.setGeburtsdatum(sdf.parse("01.01.1900"));
		} catch (ParseException e) {
			e.printStackTrace();
		}
		m.setStaatsbuergerschaft(laender.getListLaender().get(0));
		m.getAdressen().add(adr);
		m.getTelefonnummern().add(t);
		
		mitglieder.getListMitglieder().add(m);

		Mitglied m2 = MitgliederFactory.eINSTANCE.createMitglied();
		m2.setAnrede(Anrede.FRAU);
		m2.setVorname("1");
		m2.setNachname("2");
		try {
			m2.setGeburtsdatum(sdf.parse("01.01.1900"));
		} catch (ParseException e) {
			e.printStackTrace();
		}
		m2.setStaatsbuergerschaft(laender.getListLaender().get(1));
		m2.getAdressen().add(adr);
		m2.getAdressen().add(adr2);
		
		Partner p = MitgliederFactory.eINSTANCE.createPartner();
		p.setAnrede(Anrede.FRAU);
		p.setVorname("1");
		p.setNachname("2");
		p.setStaatsbuergerschaft(laender.getListLaender().get(1));
		m.setPartner(p);
		
		mitglieder.getListMitglieder().add(m2);
	}
	
	public Mitglieder getMitglieder() {
		return mitglieder;
	}
	
	public Laender getLaender() {
		return laender;
	}
}

Zugreifen tu ich dann immer über 'Laender laender = ModelProvider.INSTANCE.getLaender();'...
Ist das Sinnvoll oder Blödsinn, weil ich irgendwie direkt auf das EMF zugreifen könnte?

So, das wärs mal fürs erste, kann gern noch mehr Code liefern, falls gewünscht.

Würde mich auf jeden Fall sehr über Antworten freuen,
bis dahin noch schönes WE & lg
O
 

Wildcard

Top Contributor
Das über den Command Stack laufen zu lassen ist denkbar einfach. Statt EMFProperties verwendest du EMFEditProperties und übergibst die Editing Domain, der Rest bleibt identisch.
Für die Länder könntest du ein Enum verwenden wenn ich dich richtig verstanden habe.
 

overflow

Mitglied
Danke Wildcard für den Hinweis, prob ist auch, woher ich das EditDomain bekomme... kann ich das nur erzeugen, wenn ich mein Modell in einem XMI File ablege, oder geht das ganze auch, wenn mein Modell nur im Speicher liegt?

danke und lg
 

Wildcard

Top Contributor
Danke Wildcard für den Hinweis, prob ist auch, woher ich das EditDomain bekomme... kann ich das nur erzeugen, wenn ich mein Modell in einem XMI File ablege, oder geht das ganze auch, wenn mein Modell nur im Speicher liegt?
Nein, mit XMI hat das nichts zu tun. Die Editing Domain ist der Zugang zum Command Stack. Alle Editoren und Views die auf der gleichen Modell Instanz operieren, sollten sich die gleiche Editing Domain Teilen.
Der generierte Editor erzeugt zB eine Editing Domain und stellt sie der generierten Properties View zur Verfügung.
 

overflow

Mitglied
Hi Wildcard,

danke für deinen Tipp!
habs jetzt mit deiner (und Sir Wayne's :) ) Hilfe hinbekommen (dieser Thread)...Ich definiere also ein EditingDomain, verwende beim Binding die EMFEditProperty und kann über den BasicCommandStack undos und redos ausführen... so weit so gut :)

Jetzt wäre meine Frage: kann ich quasi ganze Bearbeitungen nur im Stack ablaufen lassen, ohne dass sie im Modell zu sehen sind, und dann mehr oder weniger mittels eines SaveAllChanges oder sowas diese übertragen?

atm ists so, dass ich im TableViewer meine Einträge habe, in einer zweiten View bearbeite ich diese. Dort wird alles auf den Commandstack geschrieben (undo/redo funzt), aber es wird wegen des bindigs direkt auch im modell gezeigt (also im TableViewer). Bisher war das wünschenswert, jetzt hätte ich aber lieber, dass die Änderungen erstmal vorgenommen werden, und dann eben durch einen save button gespeichert werden.

Geht das über den CommandStack, oder muss ich dafür statt einer View einen Editor verwenden? bzw ist mir da der unterschied nicht richtig klar... habe nur mehrfach schon den Editor gesehn.

An dieser Stelle nochmal einen herzlichen Dank vA an dich, Wildcard, dass du meinem Programmier-Schmutz immer so geduldig liest und dazu auch antwortest :) echt tolle Sache!

beste grüße!
 

Wildcard

Top Contributor
Geht das über den CommandStack, oder muss ich dafür statt einer View einen Editor verwenden? bzw ist mir da der unterschied nicht richtig klar... habe nur mehrfach schon den Editor gesehn.
Der Command Stack ist der falsche Ansatzpunkt. Dort werden Commands abgelegt die das Modell manipuliert haben, um diese Änderungen Rückwärts wieder abzuspielen. Der wichtige Punkt is: das Modell wurde bereits verändert.
Eine Möglichkeit wäre zB das du in einer View auf einer Kopie der original Modells arbeitest, damit du 2 verschiedene Instanzen hast. Das geht sehr einfach mit EcoreUtil#copy.
Die View hat dann ihren eigenen Command Stack und ihre eigene EditingDomain. Bei 'save' wendest du dann alle Commands aus dem Stack der View auf das orignal Modell an.
Ist in jedem Fall komplizierter als ein Live Edit.
Gibt es bestimmte Gründe warum du das so haben willst? In Eclipse werden die Änderung in der Properties View zB auch direkt übernommen und der entsprechende Editor wird dadurch 'dirty'.
Dann liegt es am Nutzer ob er den Editor speichern will, oder nicht.
 

overflow

Mitglied
Hey,

erstmal danke für den Input.

Mein Gedanke war es, Bearbeitungen entweder direkt (so wie von dir beschrieben und über den CommandStack) zuzulassen, oder sie über einen Dialog zu leiten, wo die Daten in übersichtlicherer Form bearbeitet werden können. Allerdings hat natürlich der Dialog die Abbrechen-Möglichkeit, und ich dachte bisher fälschlicherweise, dass mir der Commandstack quasi erlauben würde, alle Änderungen zu verwerfen. Oder anders gesagt dachte ich , dass durch das EditingDomain das ganze Bearbeiten eigentlich über eine eigene Instanz laufen würde, welche dann quasi committed oder eben verworfen werden müsste. Anscheinend war der Gedanke falsch :)

Wie würde in dem von dir beschrieben Fall gespeichert werden, wenn der ganze Editor dirty ist? Zumal ich - soweit ich das verstanden habe - nicht in einem Editor, sondern in einer View arbeite... ist das ein Problem/Design-/Denkfehler von mir, den ich besser ausbessern sollte? Oder bin ich schon wieder am Holzweg?

Danke für deine Geduldigen Erklärungen :D

Gute Nacht und lg
 

Wildcard

Top Contributor
Das normale Pattern ist: Editor bearbeitet eine Datei, View unterstützt. Der Editor hat eine Instanz des Modells (initial aus Datei geladen). Die View bearbeitet das Modell, der Editor wird dirty. Wenn nun der Editor gespeichert wird, serialisiert der Editor das Modell in die Datei.
 

overflow

Mitglied
Hi,

dh ich sollte mal von "alles im view machen" hin zu "editor und view" gehen? Werd ich mir mal ansehen, danke!

Ist der MasterDetailBlock hier das Mittel der Wahl?

danke und lg
 

Wildcard

Top Contributor
Wie gesagt, das übliche Paradigma ist: Editor editiert und View unterstützt. Da ich aber deine Anwendung nicht kenne will ich dir nicht sagen was für dich besser oder schlechter passt.
 

overflow

Mitglied
Ah, ok, danke, habs anfangs nicht ganz gerafft!

kurze Erläuterung mein Programm: stinknormale Adressverwaltung (Klassen: Personen mit Adressen & Kontaktnummern) diese hab ich bisher in eine Enum (INSTANCE) "geladen" (Persistierung gibt es noch nicht, daher dort erzeugt).

Habe dann 2 Views erzeugt, eine fungierte als "Master" (TableViewer, welche eine getPersonen() aus der Enum als Input abgerufen hat) und die Zweite war "Detail", also gebundene Felder zum Bearbeiten der Masterselektion.

Fragen dazu:
-Wird ein Editor gesondert auf der UI dargestellt? Oder is der übergeordnet, enthält eine (mehrere?) Views und verwaltet diese?
-Kann ich mein EMF Modell direkt an einen Editor binden?
-Ist dieser MasterDetailBlock (org.eclipse.ui.forms) Teil dieses Editor/Viewer Paradigmas oder ist das eine eigene Implementierung davon (evtl. nicht an ein EMF Modell bindbar?)

DANKE wieder für deinen tollen Input, das Bild in meinem Kopf nimmt immer mehr Umrisse an (Details und Farben fehlen noch, aber ich bin ja noch jung :) )

Liebe Grüße!
 
G

Gast2

Gast
Von einer View gibt es meistens nur eine Instanz von einem Editor kann es mehrere Instanzen geben die jedesmal IEditorInput entgegen nehmen.
Eclipse RCP Tutorial

EMF hat ein eigenes Databinding, welches du an widget hängen kannst ähnlich wie das JFaceBinding

Hier ein Beispiel das Undo/Redo unerstützt

Java:
EMFDatabindingContext bindingContext;
IEMFEditValueProperty prop = EMFEditProperties.value(editingDomain,
				eStructuralFeature)
bindingContext.bindValue(
				SWTObservables.observeDelayedValue(bindDelay,
						SWTObservables.observeText(textfield.getTextControl(), SWT.Modify)),
				prop.observe(eObject));

Dein Editor oder view braucht dann noch EditingDomain für undo/redo und muss IEditingDomainProvider implementieren
Java:
	protected void initializeEditingDomain() {
		// Create an adapter factory that yields item providers.
		ComposedAdapterFactory adapterFactory = new ComposedAdapterFactory(
				ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
		// Create the command stack that will notify this editor as commands are
		// executed.
		BasicCommandStack commandStack = new BasicCommandStack();
		commandStack.addCommandStackListener(new CommandStackListener() {

			@Override
			public void commandStackChanged(EventObject event) {
				fireDirtyPropertyChange();
			}
		});
		editingDomain = new AdapterFactoryEditingDomain(adapterFactory, commandStack, new HashMap<Resource, Boolean>());
	}
 

Wildcard

Top Contributor
-Wird ein Editor gesondert auf der UI dargestellt? Oder is der übergeordnet, enthält eine (mehrere?) Views und verwaltet diese?
Wenn du Eclipse verwendest, weißt du bereits wie Views und Editoren arbeiten. Der Java Editor ist ein Editor, die Outline eine View die den Editor unterstützt.
-Kann ich mein EMF Modell direkt an einen Editor binden?
Für das Modell ist egal ob es in einem Editor, View, oder beidem angezeigt wird.
-Ist dieser MasterDetailBlock (org.eclipse.ui.forms) Teil dieses Editor/Viewer Paradigmas oder ist das eine eigene Implementierung davon (evtl. nicht an ein EMF Modell bindbar?)
Ein MasterDetailBlock ist üblicherweise in einem Editor zu finden. Der Editor für die Plugin.xml enthält einen solchen Block. Der Tree/die Liste links ist Master, je nach selektion bekommst du dann rechts davon andere Optionen (Details)
 

overflow

Mitglied
Danke für die Anregungen, bin am Wochenende nicht dazu gekommen, den editor mal neu zu schreiben! Werds aber in den nächste Tagen machen, und mich dann nochmals melden!

thx und lg
Markus
 

overflow

Mitglied
Hi Leute,

danke, hab mich mal am Editor versucht, diesen aber dann verworfen, weil er verdammt kompliziert wer und mmn. recht viel Code dabei hatte, den ich eigentlich nicht brauche.

Mir ist aber jetzt ein anderes Problem aufgefallen, welches ich noch nicht gelöst habe (wenn Ihr wollt mach ich dafür gern einen neuen Thread auf):

-Mein Modell hat die Klasse Person, welche beliebig viele Adressen (Klasse Adresse) halten kann. diese werden als Verweis listAdressen gehalten.
Ich stelle dies dar, indem ich die Adressen in einem ListViewer ausgebe, und zwar wie folgt:
Java:
lvAdressen.setContentProvider(new ObservableListContentProvider());
WritableList input = new WritableList(aktPerson.getAdressen(), Adresse.class);
lvAdressen.setInput(input);
Das Problem ist jetzt, dass sich die Liste nicht automatisch aktualisiert, wenn ich über einen Button ein neues Adressenobjekt anhänge. erst mit
Code:
 lvAdressen.refresh();
wird mir das neue Objekt angezeigt. Das liegt wohl daran, dass ich mein Personenobjekt nicht direkt an den ListViewer gebunden habe.... nur: wie bewerkstellige ich das? Hab im Zusammenhang mit TableViewer öfters das ViewerSupport.bind() gesehen, dies aber nicht auf meinen ListViewer umlegen können... hat da jemand ein Beispiel oder einen Tipp dazu?

[EDIT]
Hab das selbe Problem auch eine Ebene darunter: meine TableViewer, welcher alle Personenobjekte hält, aktualisiert den Eintrag zur Adressensammlung nicht... erst auf Refresh.
Java:
ObservableListContentProvider obsListCP = new ObservableListContentProvider();
tableViewer.setContentProvider(obsListCP);

IObservableMap[] obsMap = EMFObservables.observeMaps(obsListCP.getKnownElements(), new EStructuralFeature[] { Literals.PERSON__ANREDE, Literals.PERSON__VORNAME, Literals.PERSON__NACHNAME, Literals.PERSON__GEBURTSDATUM, Literals.PERSON__STAATSBUERGERSCHAFT, Literals.PERSON__ADRESSEN, Literals.MITGLIED__PARTNER, Literals.PERSON__EMAIL });
tableViewer.setLabelProvider(new ObservableMapLabelProvider(obsMap));

IObservableList mglObsList = EMFObservables.observeList(Realm.getDefault(), mitglieder, Literals.MITGLIEDER__LIST_MITGLIEDER);
tableViewer.setInput(mglObsList);
Die Ausgabe erfolgt mithilfe eines modifizierten ColumnLabelProvider, der die erste Adresse ausgibt und weitere mit [+x] angibt. Funktioniert, allerdings werden Änderungen am ersten Adressobjekt erst nach refresh sichtbar. Wenn ich allerdings lösche, wird das erste Objekt sofort entfernt und das 2te rückt automatisch nach... nur warum ???:L :rtfm:

[/EDIT]

Vielen Dank und LG!
 
Zuletzt bearbeitet:
G

Gast2

Gast
Du brauchst kein Databinding für eine Tabelle bzw. Tree benutz einfach die richtigen Adapter die dir EMF mitgeneriert...

für eine tabelle mit einer person, die mehrere adressen hat.
Java:
	AdapterFactory adapterFactory = new MyProviderAdapterFactory();
		viewer.setLabelProvider(new AdapterFactoryLabelProvider(adapterFactory));
		viewer.setContentProvider(new AdapterFactoryContentProvider(adapterFactory));
		viewer.setInput(person);
 

overflow

Mitglied
Danke,

leider wird mir dann nichts angezeigt... muss ich da rundherum noch mehr machen?

zudem frag ich mich, wie er wissen soll, dass ich aus der Sammlung Person nur die Adressen will, und nicht zB. Vor/Nachname...

Kann ich zudem da Felder irgendwie anpassen, so wie ichs bisher über die gesonderten Labelprovider der Columns gemacht habe?

Sorry, dass ich mit sovielen gegenfragen daher komm... bin leider etwas planlos :)

lg!
 

overflow

Mitglied
Hi Leute!

#1: Danke nochmal Sir Wayne für den Fred, du hattest ja genau das selbe Problem wie ich... Die schlechte Nachricht ist aber: ich konnte es bisher noch nicht lösen...

Hier nochmal der Ansatz:

View 1 enthält nen TableViewer, welcher mehrere Personen hält.
Eine Person hat gewisse fixe Attribute (Vor-, Nachname) aber auch Listen (mehrere Adressen (Str/Plz/Ort) oder Kontaktmöglichkeiten (Vorwahl/Nr/Enum Typ).

View 2 enthält die Bearbeitungsmöglichkeiten, also einige Felder, welche via Databinding ans Modell gebunden sind. Zusätzlich sind 2 ListViewer dabei, welche die ELists Adressen und Kontakt halten.

Fall 1: Änderung eines "fixen" Attributes: sofortige Übernahme in die View 1 - Tabelle
Fall 2: Hinzufügen einer Adresse/Kontakt in der View 2: sofortige Übernahme in die View 1 - Tabelle
Fall 3:Fall 2 mit löschen - selbes Ergebnis wie Fall 2
Fall 4: Ändern der Details einer Adresse - Übernahme in View 1 erst nach manuellem refresh auf den TableViewer.

so, wie hab ich das jetzt gebunden:
View 1 - Tabelle:
Code:
adapterFactory = new MitgliederItemProviderAdapterFactory();
Code:
tableViewer.setContentProvider(new AdapterFactoryContentProvider(adapterFactory));
Code:
tableViewer.setLabelProvider(new AdapterFactoryLabelProvider(adapterFactory));
Code:
tableViewer.setInput(mitglieder);

View 2 - AdressenListViewer
Code:
lvAdressen.setContentProvider(new ObservableListContentProvider());
Code:
WritableList input = new WritableList(aktPerson.getAdressen(), Adresse.class);
Code:
lvAdressen.setInput(input);

bzw.
Java:
		AdapterFactory adapterFactory = new MitgliederItemProviderAdapterFactory();

		lvKontaktnummern.setContentProvider(new AdapterFactoryContentProvider(adapterFactory));
		lvKontaktnummern.setLabelProvider(new AdapterFactoryLabelProvider(adapterFactory));

		lvKontaktnummern.setInput(aktMitglied); 
		
		lvKontaktnummern.setLabelProvider(new LabelProvider() {
	        public Image getImage(Object element) {
	          return null;
	        }

	        public String getText(Object element) {
	        	System.out.println("Labelprovider: " + element.getClass().getName());
	        	if(element instanceof TelefonImpl)
	        		return element.toString();
	        	else
	        		return null;
	        }
	      });

Die im View2 gegangenen Wege führen zum exakt selben Ergebnis, die WritableList wird wieder entfernt und durch die eigenen Provider ersetzt. Wollte nur alle Wege probieren. Habe auch versucht, bei allen 3 Binds die gleiche
Code:
AdapterFactory adapterFactory = new MitgliederItemProviderAdapterFactory();
zu verwenden... ändert leider auch nix.

denke, dass ich die Childeinstellungen richtig habe sowie die Containment Einstellungen. Neues GenModel erstellt und neu generiert -> brachte auch nix. Jetzt bin ich erstmal Ratlos... :rtfm::autsch:

---

Zweites Problem: was ich ursprünglich hatte, dass ich in Dialogen eigene Objekte bearbeiten wollte, welche ich bei bedarf wieder verwerfen können will.

Erster Ansatz war es, dass ich ein Objekt bekomme (
Code:
aktuellePerson
) und diese dann mittels Copy (
Code:
duplikatPerson = EcoreUtil.copy(aktuellePerson);
) kopiere, bearbeite und dann entweder bei Dialog.OK
Code:
EcoreUtil.replace(aktuellePerson, duplikatPerson );
oder eben einfach nix nachliefern. Ich bilde mir fest ein, das hat mal funktioniert, aber als ichs Zuhause ausprogrammiert hab, wollte ers ums verrecken nicht replacen. Hab imo auch umkopieren und überschreiben probiert, funktioniert hat nix.

Also hab ich an WildCards Lösung mit dem CommandStack gedacht (Post 6 "Bei 'save' wendest du dann alle Commands aus dem Stack der View auf das orignal Modell an."). Habe aber keine Möglichkeit gefunden, wie ich das Ziel der Commands im Stack vom dupPerson auf die aktPerson umdrehen könnt.
Leider hab ich auch keine vernünftige Möglichkeit gefunden, den CommandStack bis zu einem gewissen Punkt zurückzuspringen (Beispiel: Wizard, der zuerst Stammdaten und dann Adressen macht. Stammdaten 3 Änderungen, dann sozusagen ein Savepoint, +x Adressänderungen, die alle undoable wären, sodass nurmehr die initialen 3 Stammdatenänderungen am Stack wären, und weiters, falls Dialog.ok diese [alles] übernehmen oder Dialog.Cancel -> undoAll)

Habs jetzt so gelöst, dass jeder Teilbereich einen eigenen CommandStackListener hat, der die Änderungen des Teilbereiches mitzählt, und diese dann undo't... imo auch nicht sehr sauber. gibtz da ne bessere Möglichkeit bzw hab ich einfach die richtigen Commands übersehen?

Danke mal soweit, vor allem das erste Problem mit dem fehlenden Aktualisieren über die 2te Ebene nervt sehr :/ wünsch euch allen was, lg

ps.: habe auch versucht, einen Teil der Adresse (einfach ersten Listeneintrag->Strasse genommen) an ein Textfeld direkt zu binden, hat auch nicht besser funktioniert... bei allen Lösungen ist nach einen refresh alles richtig, nur ohne halt nicht :/
 
G

Gast2

Gast
Mach doch erstmal ein einfaches EMFModel und probier eine einfache Tabelle mit EMFBinding anzubinden und nicht zuviel auf einmal zu machen.

Du kannst soviele Fehler gemacht haben ist schwer jetzt was dazu zu sagen...
 

Alan4

Mitglied
Hallo zusammen,
ich habe eine Frage bitte bezüglich EMF Forms.

Ich bin bis jetzt dazu bekommen, dass ich aus einer XSD ein Listobjekt nun nur Pull-Down-Menü in EMF Forms bekomme, und das wäre die Fragestellung, wie ich aus einer XSD eine ListBox in EMF Forms bekommen kann?
Ist das möglich? Oder es ist schon so festgelegt?

Außerdem könnte man die UI nach der Erstellung irgendwie als XML Beschreibung (Also XML Datei) abspeichern?

Ich bedanke mich im Voraus für eure Antwort und Unterstützung.

Beste Grüße
Alan Jaff
 
Ähnliche Java Themen

Ähnliche Java Themen


Oben