我正在创建一个将数据存储在本地h2数据库中的CRUD应用程序。我是JavaFX的新手。我使用TabPane
使用Tab
创建了jfxml
至3 Scene Builder 2.0
。每个标签都包含AncorPane
,其中包含所有控件:Label
,EditText
等。使用一个控制器管理TabPane
和Tabs
。此函数用于创建和更新数据。它是从显示所有数据的网格调用的。一个非常基本的CRUD应用程序。
我陷入了验证阶段:当用户更改选项卡时,通过选择另一个选项卡,它被称为相应选项卡的验证方法。如果选项卡的验证失败,我希望选择保留在此选项卡上。
为实现这一目标,我在ChangeListener
的{{1}}上实施了以下SelectionModel
:
TabPane
我不确定这是正确的做法,因为:
boolean processingTabValidationOnChange = false;
tabPane.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
if (processingTabValidationOnChange == false) {
boolean success;
switch (t.intValue()) {
case 0: success = validationTab1Passed();
break;
case 1: success = validationTab2Passed();
break;
case 1: success = validationTab3Passed();
break;
default: success = false;
}
if (success == false) {
processingTabValidationOnChange = true;
// select the previous tab
tabPane.getSelectionModel().select(t.intValue());
processingTabValidationOnChange = false;
}
}
}
});
被触发两次,一次用于用户选择,一次用于changed
。为了避免这种情况,我使用了一个全局字段布尔.select(t.intValue())
...我知道的很脏。processingTabValidationOnChange
TabPane显示正确的选项卡后,选项卡的内容为空,就好像隐藏了.select(t.intValue())
一样。我无法再次选择包含错误的选项卡,因为它已被选中。 任何帮助都将不胜感激。
埃尔维斯
答案 0 :(得分:2)
正如我对詹姆斯的答案所评论的那样,我正在寻找一种干净的解决方案来解决我所提出的问题。简而言之,防止用户在当前选项卡的验证失败时更改为其他选项卡。我提出了一个实施ChangeListener
的解决方案,但正如我所解释的那样:它不是很清楚&#34;清洁&#34; (小细节)它不起作用!
好的,问题是用于切换回上一个标签的代码:
tabPane.getSelectionModel().select(t.intValue());
在切换标签本身之前调用它已完成,因此它最终被选中......但是隐藏了。
为了防止这种情况,我使用了Platform.runLater()
。更改选项卡后执行代码.select()
。完整的代码变为:
//global field, to prevent validation on .select(t.intValue());
boolean skipValidationOnTabChange = false;
tabPane.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
if (skipValidationOnTabChange == false) {
boolean success;
switch (t.intValue()) {
case 0:
success = validationTab1Passed();
break;
case 1:
success = validationTab2Passed();
break;
case 1:
success = validationTab3Passed();
break;
default:
success = false;
}
if (success == false) {
Platform.runLater(new Runnable() {
@Override
public void run() {
skipValidationOnTabChange = true;
tabPane.getSelectionModel().select(t.intValue());
skipValidationOnTabChange = false;
}
});
}
}
}
});
无论如何,如果有人有更好的解决方案来实现这一点,那么欢迎你。在示例中使用consume()
之类的方法来阻止选项卡被选中两次。这样我就可以消除全局字段skipValidationOnTabChange
。
埃尔维斯
答案 1 :(得分:1)
我会以非常不同的方式处理这个问题。而不是等待用户选择不同的选项卡,并在当前选项卡的内容无效时还原,阻止用户首先更改选项卡。
Tab类有一个disableProperty。如果设置为true,则无法选择选项卡。
定义一个BooleanProperty或BooleanBinding,表示第一个选项卡中的数据是否无效。您可以根据选项卡中控件的状态创建此类绑定。然后将第二个选项卡的disableProperty绑定到它。这样,第二个选项卡会自动变为禁用或启用,因为第一个选项卡中的数据变为有效或无效。
您可以根据需要将其扩展到尽可能多的选项卡,按照逻辑指示绑定它们的属性。
更新:上面链接的示例现在有点简单了。它将根据字段是否有效动态更改文本字段的颜色,并通过控制器中的绑定定义验证规则。此外,每页顶部都有标题窗格,标题显示页面上的验证错误数,以及展开标题窗格时的消息列表。所有这些都动态地绑定到控件中的值,因此它为用户提供了持续,清晰但不显眼的反馈。
答案 2 :(得分:0)
我需要实现类似的事情。我通过覆盖com.sun.javafx.scene.control.behavior.TabPaneBehaviour
方法更改selectTab
类来完成此操作:
class ValidatingTabPaneBehavior extends TabPaneBehavior {
//constructors etc...
@Override
public void selectTab(Tab tab) {
try {
Tab current = getControl().getSelectionModel().getSelectedItem();
if (current instanceof ValidatingTab) {
((ValidatingTab) current).validate();
}
//this is the method we want to prevent from running in case of error in validation
super.selectTab(tab);
}catch (ValidationException ex) {
//show alert or do nothing tab won't be changed
}
}
});
ValidatingTab
是我自己Tab
的扩展名:
public class ValidatingTab extends Tab {
public void validate() throws ValidationException {
//validation
}
}
这是&#34;清洁部分&#34;诀窍。现在我们需要将ValidatingTabPaneBehavior
放入TabPane。
首先,您需要将整个com.sun.javafx.scene.control.skin.TabPaneSkin
复制(!)到新类,以便更改其构造函数。这是一个很长的类,所以这里只是切换Behavior类时的部分:
public class ValidationTabPaneSkin extends BehaviorSkinBase<TabPane, TabPaneBehavior> {
//copied private fields
public ValidationTabPaneSkin(TabPane tabPane) {
super(tabPane, new ValidationTabPaneBehavior(tabPane));
//the rest of the copied constructor
}
最后一件事是更改tabPane实例中的皮肤:
tabPane.setSkin(new ValidationTabPaneSkin(tabPane));