与嵌套控制器共享模型 [英] Share model with nested controller

查看:132
本文介绍了与嵌套控制器共享模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用SceneBuilder使用JavaFX构建一个简单的GUI,我在其中使用MenuItem(在 Main.fxml 中)来选择根文件夹。然后文件夹的内容列在TextArea中,TextArea再次包装在TabPane中( FileListTab.fxml ,嵌套的FXML包含在 Main.fxml )。

I'm trying to build a simple GUI with JavaFX using SceneBuilder, where I'm using a MenuItem (in Main.fxml) to select a root folder. The folder's contents are then listed in a TextArea that again is wrapped in a TabPane (FileListTab.fxml, nested FXML that is included in Main.fxml).

我用这篇文章作为习惯于MVC的起点。不幸的是,我不知道如何使我的嵌套FXML监听或绑定到外部,因为我没有明确地调用它。现在我只是在标签中显示我选择的文件夹。

I used this post as a starting point to get used to MVC. Unfortunately I don't know how to make my nested FXML listen or be bound to the outer one since I'm not explicitly calling it. Right now I'm stuck just to display my chosen folder in a label.

我的最小工作代码现在看起来像这样:

My minimal working code right now looks like this:

Main.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="MainController">
   <top>
      <MenuBar BorderPane.alignment="CENTER">
        <menus>
          <Menu mnemonicParsing="false" text="File">
            <items>
                  <MenuItem mnemonicParsing="false" onAction="#browseInputFolder" text="Open folder" />
            </items>
          </Menu>
        </menus>
      </MenuBar>
   </top>
   <center>
      <TabPane prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE" BorderPane.alignment="CENTER">
        <tabs>
          <Tab text="File listing">
            <content>
                <fx:include fx:id="analysisTab" source="FileListTab.fxml" />
            </content>
          </Tab>
        </tabs>
      </TabPane>
   </center>
</BorderPane>

FileListTab.fxml

<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" spacing="15.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="FileListController">
   <children>
      <HBox spacing="10.0">
         <children>
            <Label minWidth="100.0" text="Root folder:" />
            <Label fx:id="label_rootFolder" />
         </children>
      </HBox>
      <TextArea prefHeight="200.0" prefWidth="200.0" />
      <HBox spacing="10.0">
         <children>
            <Label minWidth="100.0" text="Found files:" />
            <Label fx:id="label_filesFound" />
         </children>
      </HBox>
   </children>
   <padding>
      <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
   </padding>
</VBox>

Model.java (应该是控制器之间的共享模型)

Model.java (the supposed to be shared model between controllers)

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Model {
    private StringProperty rootFolder;

    public String getRootFolder() {
        return rootFolderProperty().get();
    }

    public StringProperty rootFolderProperty() {
        if (rootFolder == null)
            rootFolder = new SimpleStringProperty();
        return rootFolder;
    }

    public void setRootFolder(String rootFolder) {
        this.rootFolderProperty().set(rootFolder);
    }
}

NestedGUI.java (主类)

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

import java.io.IOException;

public class NestedGUI extends Application {
    Model model = new Model();

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        Parent root = null;
        try {
            FXMLLoader fxmlLoader = new FXMLLoader();
            fxmlLoader.setLocation(getClass().getClassLoader().getResource("Main.fxml"));
            root = (BorderPane) fxmlLoader.load();
            MainController controller = fxmlLoader.getController();
            controller.setModel(model);

         // This openes another window with the tab's content that is actually displaying the selected root folder
/*            FXMLLoader fxmlLoader2 = new FXMLLoader();
            fxmlLoader2.setLocation(getClass().getClassLoader().getResource("FileListTab.fxml"));
            VBox vBox = (VBox) fxmlLoader2.load();
            FileListController listController = fxmlLoader2.getController();
            listController.setModel(model);

            Scene scene = new Scene(vBox);
            Stage stage = new Stage();
            stage.setScene(scene);
            stage.show();*/

        } catch (IOException e) {
            e.printStackTrace();
        }

        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }
}

MainController.java

import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;

import java.io.File;

public class MainController {
    Model model;

    public void setModel(Model model) {
        this.model = model;
    }

    public void browseInputFolder() {
        DirectoryChooser chooser = new DirectoryChooser();
        chooser.setTitle("Select folder");
        File folder = chooser.showDialog(new Stage());
        if (folder == null)
            return;

        String inputFolderPath = folder.getAbsolutePath() + File.separator;
        model.setRootFolder(inputFolderPath);
        System.out.print(inputFolderPath);
    }
}

FileListController.java

import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class FileListController {
    Model model;

    @FXML
    Label label_rootFolder;

    public void setModel(Model model) {
        label_rootFolder.textProperty().unbind();
        this.model = model;
        label_rootFolder.textProperty().bind(model.rootFolderProperty());
    }
}

我在SO上查看了各种帖子,但要么我不明白答案或其他人有不同的问题。
有人可以给我一些指示吗? (提示解决这个问题,代码片段,链接...)它看起来像一个非常基本的FXML问题,但我只是不明白。

I looked through various posts here on SO, but either I didn't understand the answers or others had different problems. Can somebody give me some pointers? (hints to solve this, code snippets, links...) It looks like a pretty basic FXML-problem, but I just don't get it.

推荐答案

我更喜欢的选项是使用自定义组件,而不是< fx:include fx:id =analysisTabsource =FileListTab.fxml/> 。因此,在 Main.fxml 中,将< fx:include> 行替换为:

An option I prefer is to use a custom component, instead of the <fx:include fx:id="analysisTab" source="FileListTab.fxml" />. So in Main.fxml, replace the <fx:include> line with:

<FileList fx:id="fileList"></FileList>

FileList 是我们新的自定义组件。您还必须将<?import yourpackage。*?> 添加到 Main.fxml 的顶部,以进行制作FXML可以使用 yourpackage 的类。 (显然, yourpackage 是包含此问题中所有类和文件的包。)

FileList is our new, custom component. You must also add <?import yourpackage.*?> to the top of Main.fxml, to make the classes of yourpackage available to FXML. (Obviously, yourpackage is the package containing all the classes and files in this question.)

下面添加的是 yourpackage.FileList.java 中自定义组件的类;主要是来自 FileListController 的代码+加载FXML所需的代码。但请注意,它扩展了JavaFX组件 VBox ,使其成为FXML组件。 VBox FileListTab.fxml 中的根组件,还必须在中声明键入 FileList.fxml 的属性

Add below is the class for the custom component in yourpackage.FileList.java; mostly your code from the FileListController + the code required to load the FXML. Note however that it extends a JavaFX component, the VBox, making it a FXML component itself. The VBox was the root component in your FileListTab.fxml and must also be declared in the type attribute of the FileList.fxml below.

package yourpackage;

import java.io.IOException;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;

public class FileList extends VBox {

    private Model model; // NOT REALLY NEEDED! KEEPING IT BECAUSE YOUR FileListController HAD IT TOO...

    @FXML
    Label label_rootFolder;

    public FileList() {
        java.net.URL url = getClass().getResource("/yourpackage/FileList.fxml");
        FXMLLoader fxmlLoader = new FXMLLoader(url);
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);

        try {
            fxmlLoader.load();
        }
        catch( IOException e ) {
            throw new RuntimeException(e);
        }
    }

    public void setModel(Model model) {
        label_rootFolder.textProperty().unbind();
        this.model = model; // NOT REALLY NEEDED!
        label_rootFolder.textProperty().bind(model.rootFolderProperty());
    }
}

以及 FileList.fxml 。这是你自己的 FileListTab.fxml ,它的根 VBox 节点由 fx替换: root type =javafx.scene.layout.VBox属性,保持所有其他属性不变:

And the FileList.fxml. This is your own FileListTab.fxml, having its root VBox node replaced by the fx:root and the type="javafx.scene.layout.VBox" attribute, keeping all other attributes the same:

<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<fx:root xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2"
    type="javafx.scene.layout.VBox"
    maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity"
    minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" spacing="15.0"
>
    <children>
        <HBox spacing="10.0">
            <children>
                <Label minWidth="100.0" text="Root folder:" />
                <Label fx:id="label_rootFolder" />
            </children>
        </HBox>
        <TextArea prefHeight="200.0" prefWidth="200.0" />
        <HBox spacing="10.0">
            <children>
                <Label minWidth="100.0" text="Found files:" />
                <Label fx:id="label_filesFound" />
            </children>
        </HBox>
    </children>
    <padding>
        <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
    </padding>
</fx:root>

您是否注意到 fx:id in上面的< FileList> 组件?您现在可以将它注入 MainController

Did you notice the fx:id in the <FileList> component above? You can now inject it in the MainController:

@FXML
private FileList fileList;

并传播 setModel()电话:

// in MainController
public void setModel(Model model) {
    this.model = model;
    this.fileList.setModel(model);
}

这篇关于与嵌套控制器共享模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆