如何在运行时定义 fx:included 表单的控制器



i有一个主式(mainform.fxml(,其控制器在FXML文件中定义。在同一fxml文件中,我有2个subforms(subform1.fxml和subform2.fxml(,我包含在fx:inclage中。Subform1具有混凝土控制器。Subform2是一种通用的"选择和编辑"表单,其背后的抽象代码。我想根据上下文显示具有抽象代码的不同具体实现的Subfort2。如果我在FXML中定义控制器,则不再是通用的。

我只使用fxmlloader来加载主形式,我找不到更改子形式控制器的任何方法。我四处走动,尝试不同的事情。任何帮助将不胜感激。

更新到我的问题感谢James_D到目前为止的帮助。我的subform1在fxml文件中的定义:

    <children>
         <!--<fx:include source="Subform1.fxml" />-->
         <!-- <Subform1 controller="${ISubform}" /> -->
         <Subform1 controller="${Subform1Controller}" />
         <!-- <Subform1 /> -->
    </children>

我创建了一个接口,如下所示:

package testsubforms;
public interface ISubform {
}

这是我的控制器:

package testsubforms;
public class Subform1Controller implements ISubform {
    public Subform1Controller() {
        System.out.println("Inside Subform1Controller");
    }
}

以下是我的subform1类:

package testsubforms;
import java.io.IOException;
import javafx.beans.NamedArg;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.GridPane;
public class Subform1 extends GridPane {
    private ObjectProperty controller;
    public ObjectProperty controllerProperty() {
        return this.controller;
    }
    public void setController(Subform1Controller controller) {
        this.controllerProperty().set(controller);
    }

    public Subform1(@NamedArg("controller") Subform1Controller controller) throws IOException {
        this.controller = new SimpleObjectProperty(this, "controller", controller);
        FXMLLoader loader = new FXMLLoader(getClass().getResource("Subform1.fxml"));
        loader.setRoot(this);
        loader.setController(controller);
        loader.load();
    }
    public Subform1() throws IOException {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("Subform1.fxml"));
        loader.setRoot(this);
        loader.setController(this);
        loader.load();
    }
}

我当前的问题是运行时错误" javafx.fxml.loadexception:无法绑定到未构图的对象",我在FXML文件中指定了Subform1。在拼图中将最后一件作品上班的任何帮助将不胜感激。一旦我得到最后一件作品,我将发布一个完整的示例,以供其他人使用。

一种方法是将接口指定为Subform2.fxml中的控制器类。例如,定义

public interface Subform2Controller {
}

然后,您可以将该接口指定为控制器"类":

<GridPane xmlns="..." fx:controller="my.package.Subform2Controller">
    <!-- -->
</GridPane>

现在指定专门处理该情况的控制器工厂:

Object subform2Controller = /* any controller implementation you like... */ ;
FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/main.fxml"));
loader.setControllerFactory(type -> {
    try {
        if (type == Subform2Controller.class) {
            return subform2Controller ;
        }
        // default implementation:
        return type.newInstance();
    } catch (Exception exc) {
        // this is pretty much fatal...
        throw new RuntimeException(exc);
    }
});
Parent root = loader.load();

这里的想法是,控制器工厂是FXMLLoader用来将FXML文件中声明的类映射到特定对象的函数。(默认情况下,它仅在指定的Class上调用newInstance()。(当您使用FXML Include时,控制器工厂会传播到加载随附的文件。此实现只是截获定义接口并返回您在代码中动态指定的任何对象的特定情况。

。据我所知,返回的对象是指定的类实例的实际要求(尽管我想我从未对此进行过测试(。无论如何,如果您确实确保控制器是实现fx:controller属性中声明的界面的类实例,则可以帮助您的理智(这也使您有机会指定您期望该控制器提供的任何功能(。

另一种方法是使用FXML"自定义组件"模式。这本质上是逆转了FXML和控制器的创建角色,这意味着您没有加载FXML文件,该文件或多或少地默默地创建控制器实例,而是创建了一个用作控制器的Java对象,并且负责加载负载fxml。

使用这种方法,您可以创建多个加载相同FXML文件的多个"自定义组件"。

所以,如果您有一个看起来像:

Subform2.fxml
<!-- headers, etc -->
<GridPane xmlns="..." fx:controller="...">
    <!-- -->
</GridPane>

您将用:

替换根元素
<!-- headers, etc -->
<fx:root type="GridPane" xmlns="..." >
    <!-- -->
</fx:root>

注意控制器不再在此处指定。

现在,您可以创建一个类似控制器的类,它只需要扩展GridPane(或更一般而言,它扩展了在fx:root元素的"类型"属性中指定的类(。在构造函数中,为FXML文件创建FXMLLoader,然后将根和控制器设置为当前对象:

public class Subform2 extends GridPane {
    @FXML
    private TextField someTextField ;
    // etc
    public Subform2() throws IOException {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/path/to/Subform2.fxml"));
        loader.setRoot(this);
        loader.setController(this);
        loader.load();
    }
    @FXML
    public void handleSomeEvent(ActionEvent event) {
        // ...
    }
    // ...
}

要使用它,您可以在Java中使用

在Java中这样做
GridPane subform2 = new Subform2();

如果要在FXML中使用它,而不是使用<fx:include>,只需使用常规实例元素即可。当然,您可以像往常一样指定任何属性,无论它们是从GridPane继承还是在类本身中定义它们:

<Subform2 alignment="center">
    <padding>
        <Insets top="5" right="5" bottom="5" left="5"/>
    </padding>
</Subform2>

这可能会满足您的需求,因为您只能定义一个FXML文件,但任意许多类似的类别都加载了单个FXML文件。

作为此的轻微变体,您可以简单地将GridPane子类作为FXML的根,然后将另一个对象传递给其构造函数,以充当控制器。因此,例如,如果您定义了代表FXML文件控制器的接口(或抽象类(:

public interface Subform2Controller {
    /* methods */
}

您可以做

public class Subform2 extends GridPane {
    public Subform2(@NamedArg("controller") Subform2Controller controller) throws IOException {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/path/to/Subform2.fxml"));
            loader.setRoot(this);
            loader.setController(controller);
            loader.load();
        }
    }
}

这使您可以做

之类的事情
<Subform2 controller="${subform2Controller}" />

再次,您可以加载FXML文件并动态指定控制器。

相关内容

  • 没有找到相关文章

最新更新