当前位置: 首页 > 面试题库 >

与嵌套控制器共享模型

蒯安平
2023-03-14
问题内容

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

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

我现在最小的工作代码如下所示:

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 (应该在控制器之间共享的模型)

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问题,但是我不明白。


问题答案:

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

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

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

在下面添加的是yourpackage.FileList.java;中的自定义组件的类。您的代码大部分来自FileListController+加载FXML所需的代码。但是请注意,它扩展了JavaFX组件VBox,使它本身成为FXML组件。该VBox是你的根组件FileListTab.fxml,因此也必须在声明type的属性FileList.fxml如下。

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,其根节点VBoxfx:roottype="javafx.scene.layout.VBox"属性替换,而所有其他属性保持相同:

<?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<FileList>上面的组件中注意到了吗?您现在可以将其注入MainController

@FXML
private FileList fileList;

并传播setModel()呼叫:

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


 类似资料:
  • 问题内容: 我有一个抽象的父视图,该视图旨在与其嵌套视图共享一个控制器。 路由工作正常。 问题是,当我从其中一个嵌套视图更新变量时,更改未反映在视图中。当我从父视图执行相同操作时,它可以正常工作。这种情况不需要。 我的猜测是正在为每个视图创建一个新的实例,但是我不确定为什么或如何修复它。 问题答案: 这里的问题与以下问答有关:如何在angularjs ui-router中的状态之间共享$scope

  • 使用控制器工厂的问题: 到目前为止,我最好的方法是使用Stephen Chin方法连接顶层控制器。 对于有多个bean实例的情况,父控制器将通过@autowire/@qualifer获取对特定bean的引用,然后在相应的控制器上设置。 下一级控制器也可以通过在顶层控制器上公开它们并调用autowire()来连接 有没有使用控制器工厂机制,这样我就可以在spring上下文中定义控制器,以便更容易地将

  • Java FXML文档要求创建两个变量,如下所示: 什么决定控制器变量名?是否总是只有include id后跟“controller”?

  • 问题内容: 我正在阅读有关Angular验证的文章,并认为在我自己的项目中使用它会很好。它运行得非常好,我想在成功验证表单后在其他控制器中扩展它的访问方法。我尝试了各种方法来执行此操作,但似乎看不到$ scope对象中的方法。 我希望有人能告诉我我想念的东西才能完成这项工作。我叉了个笨蛋。 问题答案: 正确的方法是使用角度服务。例如: 这样,您可以在注入到的任何控制器中访问内部功能: 看到这个演示

  • 问题内容: 我有一项从服务器中获取一些客户端数据的服务: 然后在一个控制器中执行: 一切都很好。但是,我正在尝试从该服务上的另一个控制器进行监视,以在数据更改时更新其范围,而不是必须重新启动http请求: 我现在只是提醒,但从未触发过。页面最初加载时,它会发出“未定义”警报。我在控制台中没有任何错误,并且所有$ injects都很好,但是似乎从未意识到服务中的数据已更改。我在手表上做错了吗? 非常

  • 问题内容: 我正在尝试在控制器之间共享数据。用例是一个多步骤的表单,在一个输入中输入的数据以后会在原始控制器之外的多个显示位置中使用。下面和jsfiddle中的代码。 的HTML JS 任何帮助是极大的赞赏。 问题答案: 一个简单的解决方案是让您的工厂返回一个对象,并让您的控制器使用对同一对象的引用: JS: HTML: 演示: http : //jsfiddle.net/HEdJF/ 当应用程序