2017-05-04 7 views
0

Ich möchte eine Anwendung mit JavaFX erstellen und ich habe ein Problem mit getrennten Ansichten (gespalten von afterburner.fx) und dem mvvm Prinzipal. In meinem Szenario habe ich eine Ansicht mit einem Textfeld und einer Schaltfläche und eine zweite Ansicht mit einer Listenansicht. Jetzt schreibe ich etwas Text in das Textfeld, um den Text zur Liste hinzuzufügen, indem ich auf die Schaltfläche klicke. Ich habe vier Klassen (View und Viewmodel für Textfeld/Schaltfläche und Liste). Jetzt die Frage: Wie kann ich auf den Buttonklick reagieren und den Text aus dem Textfeld in die Liste übernehmen?Übertragen Sie einen Wert von ViewModel zu einem anderen ViewModel (JavaFX, mvvm, afterburner.fx)

Ich kann den Text in der Ansicht und eine Eigenschaft im ViewModel binden. Aber wie kann ich den Wert auf das Viewmodel der Liste übertragen?

Ich hoffe, jemand kann mir mit diesem Verständnis Problem helfen.

Vielen Dank!

Antwort

0

Die Grundidee besteht darin, ein einziges Datenmodell zu verwenden, das unter den Komponenten aufgeteilt ist, die auf gemeinsam genutzte Daten zugreifen müssen. Die Verwendung von @Inject in afterburner.fx bietet eine bequeme Möglichkeit, dies für Sie zu verwalten.

Bei Entwurfsmustern ist zunächst zu beachten, dass afterburner.fx (und in geringerem Maße vielleicht auch der FXML- "Controller" -Mechanismus im Allgemeinen) tatsächlich auf einem MVP-Muster basiert (kein MVVM-Muster). . Die FXML-Datei repräsentiert die (passive) Ansicht; Der Präsentator ist natürlich der Moderator und das Modell wird entsprechend den Anwendungsanforderungen implementiert.

Normalerweise ist das Framework, das Sie verwenden, ein großer Teil der Entscheidung, welche Muster zu verwenden sind, daher denke ich, dass meine wichtigste Empfehlung darin besteht, ein MVP-Muster zu verwenden. Mit MVP, das sieht wie folgt aus:

Sie können ein ObservableList in Ihrem Modell erstellen:

public class Model { 

    private final ObservableList<String> itemList = FXCollections.observableArrayList(); 

    public ObservableList<String> getItemList() { 
     return itemList ; 
    } 

} 

Dann in dem Moderator für die Ansicht, dass die ListView hat, tun:

public class ListPresenter { 

    @Inject 
    private Model model ; 

    @FXML 
    private ListView<String> listView ; 

    @FXML 
    public void initialize() { 
     listView.setItems(model.getItemList()); 
    } 
} 

so dass das ListView das ObservableList im Modell für seine Unterstützungsliste von Einzelteilen verwendet. Dies bedeutet, dass sich die ListView automatisch aktualisiert, wenn sich diese Liste ändert.

In den Vortragenden für die Ansicht, dass das Textfeld hat, was Sie tun:

public class AddItemPresenter { 

    @Inject 
    private Model model ; 

    @FXML 
    private TextField textField ; 

    // handler for button press: 
    @FXML 
    private void handleButton() { 
     model.getItemList().add(textField.getText()); 
    } 
} 

Nun, wenn die Taste gedrückt wird, wird der Text in das Textfeld ein, um die Artikelliste des Modells hinzugefügt. Da Sie diese Liste als Hintergrundliste für die Listenansicht festlegen, wird der Text in der Listenansicht angezeigt.


Wenn Sie hier ein MVVM Muster zu zwingen, ich glaube, Sie haben wie umfassend das FXML-Moderator Paar von Nachbrenner „Ansicht“ in MVVM zu betrachten. Sie erhalten also etwas, das wie ein M (VP) VM-Muster aussieht. Ich bin mit MVVM nicht so vertraut wie mit MVC und MVP, aber nach meinem Verständnis gibt es drei Komponenten: das bekannte Datenmodell (M), die Ansicht (V) und das Ansichtsmodell (VM). Die Rolle des Ansichtsmodells besteht darin, den Zustand der Ansicht einschließlich der Aktionen darzustellen, aber es weiß nichts über die tatsächliche Ansicht selbst. Im obigen Beispiel hätten Sie zwei Ansichtsmodelle. eine für jede der Ansichten. Beachten Sie, dass ein Datenmodell (M) noch vorhanden ist und die Ansichtsmodelle natürlich Zugriff darauf benötigen.Sie müssen dieselbe Datenmodellinstanz verwenden, damit dieselben Daten darauf zugreifen und dieselben Daten bearbeiten können.

Ich denke, eine Implementierung dieser sieht aus wie

public class ListViewModel { 

    @Inject 
    private Model model ; 

    private final StringProperty selectedItem = new SimpleStringProperty(); 

    public ObservableList<String> getItems() { 
     return model.getItemList(); 
    } 

    public StringProperty selectedItemProperty() { 
     return selectedItem ; 
    } 

    public String getSelectedItem() { 
     return selectedItemProperty().get(); 
    } 

    public void setSelectedItem(String item) { 
     selectedItemProperty().set(item); 
    } 
} 

Die Model Klasse die gleiche wie zuvor. Die Aufgabe des Moderators (Blick in MVVM) ist die Ansicht auf die Ansicht Modell zu binden:

public class ListViewPresenter { 

    @Inject 
    private ListViewModel viewModel ; 

    @FXML 
    private ListView<String> listView ; 

    @FXML 
    public void initialize() { 
     listView.setItems(viewModel.getItems()); 
     viewModel.selectedItemProperty().bind(listView.getSelectionModel().selectedItemProperty()); 
    } 
} 

ähnlich das View-Modell für das Modul "add Punkt" sieht aus wie

public class AddItemViewModel { 

    @Inject 
    private Model model ; 

    private final StringProperty currentItem = new SimpleStringProperty(); 

    private final Runnable addItemAction = this::addItem ; 

    public StringProperty currentItemProperty() { 
     return currentItem ; 
    } 

    public String getCurrentItem() { 
     return currentItemProperty().get(); 
    } 

    public void setCurrentItem(String item) { 
     currentItemProperty().set(item); 
    } 

    public Runnable getAddItemAction() { 
     return addItemAction ; 
    } 

    private void addItem() { 
     model.getItemList().add(getCurrentItem()); 
     setCurrentItem(""); 
    } 
} 

und die AddItemPresenter jetzt sieht aus wie

public class AddItemPresenter { 

    @Inject 
    private AddItemViewModel viewModel ; 

    @FXML 
    private TextField textField ; 

    @FXML 
    private Button addButton ; 

    @FXML 
    public void initialize() { 
     viewModel.currentItemProperty().bindBidirectional(textField.textProperty()); 
     addButton.setOnAction(e -> viewModel.getAddItemAction().run()); 
    } 
} 

der hier Effekt wirklich nur ist eine zusätzliche Schicht zwischen der Ansicht und dem (Daten) Modell einzufügen. Der Nettoeffekt fühlt sich an, als würde der Moderator nicht wirklich "an seinem Gewicht" ziehen, was normalerweise ein Anzeichen dafür ist, dass die Anwendung überdimensioniert ist und zu viele Schichten hat. Sie haben jedoch den Vorteil, dass das View-Modell jetzt viel "testfreundlicher" ist als der Presenter (und dies kann natürlich durch Bereitstellung eines Setter- oder Konstruktor-Parameters für das injizierte Modell noch verbessert werden).

+0

Danke für die Antwort, aber ich interpretiere die Moderatoren als die Ansichten im Model-View-ViewModel-Prinzip. Sie haben also keine View-Logik. Sie verbinden nur die Ansichtselemente wie das ListView mit dem ViewModel. In dem Beispiel haben die Presenter Logik listView.setItems (model.getItemList()); model.getItemList(). Add (textField.getText()); und das ist nicht wirklich testfreundlich (weil @Inject und @FXML). Meiner Meinung nach müssen die Viewmodels mit dieser Kommunikation umgehen. Ist das eine falsche Überlegung? THX – user2558928

+0

@ user2558928 Das zugrunde liegende Muster, das von afterburner.fx verwendet wird, ist ein MVP (passive view) -Muster (das ist ziemlich wahr, wenn Sie auch FXML und "Controller" -Klassen verwenden). Einen MVVM-Entwurf darüber hinaus zu erzwingen, wird schwierig werden - Sie würden mit einer Art M (VP) VM Design enden. Vielleicht könnten Sie Ihrer Frage Code hinzufügen, um zu zeigen, von was Sie ausgehen? –

+0

@ user2558928 Siehe Update mit einem MVVM-Ansatz (denke ich ...). –

Verwandte Themen