如何将模型 属性 与 TreeView 单元格中的复选框状态相关联?
How to associate a model property with a checkbox state in a TreeView cell?
我正在用 JavaFX 编写一个简单的任务管理器应用程序。以下是我到目前为止编写的简单代码
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBoxTreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.CheckBoxTreeCell;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class App extends Application {
class TaskClass {
public String task_Title;
public String task_Duration;
public String Remained_Days;
public boolean task_Is_Completed;
}
@Override
public void start(Stage primaryStage) {
TaskClass TaskClassObject = new TaskClass();
TaskClassObject.task_Title = "Buy a Car";
TaskClassObject.task_Duration = "7 days";
TaskClassObject.Remained_Days = "2 days";
TaskClassObject.task_Is_Completed = false;
TreeView<TaskClass> treeView = new TreeView<TaskClass>();
CheckBoxTreeItem<TaskClass> rootNode = new CheckBoxTreeItem<TaskClass>(TaskClassObject);
treeView.setRoot(rootNode);
treeView.setCellFactory(CheckBoxTreeCell.<TaskClass>forTreeView());
VBox vb = new VBox();
VBox.setVgrow(treeView, Priority.ALWAYS);
vb.getChildren().add(treeView);
Scene Scene = new Scene(vb, 800, 600);
primaryStage.setTitle("my task manager app");
primaryStage.setScene(Scene);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}
我想遍历 treeView
个节点并为每个节点获取 TaskClassObject
并访问其字段(TaskClassObject.task_Title
和其他字段)并根据 [=15 选中复选框=].如果为真,则应选中节点中的复选框。
澄清有关方法的信息
您说:
I want to iterate over treeView nodes and get the TaskClassObject for each node and access its fields (TaskClassObject.task_Title and other fields) and checkbox gets checked based on TaskClassObject.task_Is_Completed. if its true, checkbox in node should be checked.
然而,那不是你真正想做的。您不想尝试遍历事物并担心像 TreeView 这样的虚拟化控件中的节点和东西。
GUI 编程是 event-driven,您要做的是响应事件并根据事件更新关联的模型状态。
你真正需要问(并且想要)的是:
How do I bind the selected value from a CheckBoxTreeItem to my model class, so that when a CheckBox in an associated CheckBoxTreeCell is selected or unselected my associated property in my model class is updated to reflect the new value?
(我将您的问题标题更新为其中的一个子集,以便其他人遇到相同问题时可以轻松找到它)。
如何实现需要的绑定
CheckBoxTreeItem
和 CheckBoxTreeCell
class 相当 high-level class 被设置为响应适当的事件并根据更新模型状态在他们之上。您需要利用它们提供的 high-level 接口来使它们真正可用。要为您的示例执行此操作,您需要:
使您的模型 class(Task
)使用并公开 JavaFX 属性,以便 UI 可以根据 属性 自动更新UI 的值和更新可以自动更新 属性 值。
将任务实例中的 complete
属性 绑定到关联的 CheckBoxTreeItem
。这是通过 bi-directional 绑定完成的,因此 Task
中完整 属性 的更新将更新 UI 和 vice-versa.[=24 中的选择状态=]
task.completeProperty().bindBidirectional(treeItem.selectedProperty());
向 CheckBoxTreeCell
提供选定的 属性 绑定回调,以将其与您的 CheckBoxTreeItem
相关联。这实际上通常默认发生,但是,如果您还想要一个 StringConverter(在这种情况下您这样做),那么据我所知,您需要手动提供一个。
为 CheckBoxTreeCell
提供一个 StringConverter,以便它能够以清晰的方式显示您的 Task
。
示例代码
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.CheckBoxTreeItem;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.CheckBoxTreeCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.StringConverter;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
public class App extends Application {
public static void main(String[] args) {
launch();
}
@Override
public void start(Stage stage) {
Task task = createTask();
CheckBoxTreeItem<Task> treeItem = new CheckBoxTreeItem<>(task);
task.completeProperty().bindBidirectional(treeItem.selectedProperty());
TreeView<Task> treeView = new TreeView<>();
attachTaskCellFactory(treeView);
treeView.setRoot(treeItem);
StackPane layout = new StackPane(treeView);
Scene Scene = new Scene(layout);
stage.setScene(Scene);
stage.show();
}
private void attachTaskCellFactory(TreeView<Task> treeView) {
Callback<TreeItem<Task>, ObservableValue<Boolean>> getSelectedProperty =
item -> {
if (item instanceof CheckBoxTreeItem<?>) {
return ((CheckBoxTreeItem<?>)item).selectedProperty();
}
return null;
};
StringConverter<TreeItem<Task>> treeItemStringConverter = new StringConverter<>() {
@Override
public String toString(TreeItem<Task> treeItem) {
return treeItem == null || treeItem.getValue() == null || treeItem.getValue().getTitle() == null
? ""
: treeItem.getValue().getTitle();
}
@Override
public TreeItem<Task> fromString(String string) {
// not actually used, only provided to allow a concrete instance to be created.
return null;
}
};
treeView.setCellFactory(
CheckBoxTreeCell.forTreeView(
getSelectedProperty,
treeItemStringConverter
)
);
}
private Task createTask() {
Task task = new Task(
"Buy a Car",
Duration.of(7, ChronoUnit.DAYS),
Duration.of(2, ChronoUnit.DAYS),
false
);
task.completeProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("Task complete: " + newValue);
});
return task;
}
public class Task {
final private StringProperty title = new SimpleStringProperty();
final private ObjectProperty<Duration> duration = new SimpleObjectProperty<>();
final private ObjectProperty<Duration> remainingDays = new SimpleObjectProperty<>();
final private BooleanProperty complete = new SimpleBooleanProperty();
public Task(String title, Duration duration, Duration remainingDays, Boolean complete) {
setTitle(title);
setDuration(duration);
setRemainingDays(remainingDays);
setComplete(complete);
}
public String getTitle() {
return title.get();
}
public StringProperty titleProperty() {
return title;
}
public void setTitle(String title) {
this.title.set(title);
}
public Duration getDuration() {
return duration.get();
}
public ObjectProperty<Duration> durationProperty() {
return duration;
}
public void setDuration(Duration duration) {
this.duration.set(duration);
}
public Duration getRemainingDays() {
return remainingDays.get();
}
public ObjectProperty<Duration> remainingDaysProperty() {
return remainingDays;
}
public void setRemainingDays(Duration remainingDays) {
this.remainingDays.set(remainingDays);
}
public boolean isComplete() {
return complete.get();
}
public BooleanProperty completeProperty() {
return complete;
}
public void setComplete(boolean complete) {
this.complete.set(complete);
}
}
}
我正在用 JavaFX 编写一个简单的任务管理器应用程序。以下是我到目前为止编写的简单代码
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBoxTreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.CheckBoxTreeCell;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class App extends Application {
class TaskClass {
public String task_Title;
public String task_Duration;
public String Remained_Days;
public boolean task_Is_Completed;
}
@Override
public void start(Stage primaryStage) {
TaskClass TaskClassObject = new TaskClass();
TaskClassObject.task_Title = "Buy a Car";
TaskClassObject.task_Duration = "7 days";
TaskClassObject.Remained_Days = "2 days";
TaskClassObject.task_Is_Completed = false;
TreeView<TaskClass> treeView = new TreeView<TaskClass>();
CheckBoxTreeItem<TaskClass> rootNode = new CheckBoxTreeItem<TaskClass>(TaskClassObject);
treeView.setRoot(rootNode);
treeView.setCellFactory(CheckBoxTreeCell.<TaskClass>forTreeView());
VBox vb = new VBox();
VBox.setVgrow(treeView, Priority.ALWAYS);
vb.getChildren().add(treeView);
Scene Scene = new Scene(vb, 800, 600);
primaryStage.setTitle("my task manager app");
primaryStage.setScene(Scene);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}
我想遍历 treeView
个节点并为每个节点获取 TaskClassObject
并访问其字段(TaskClassObject.task_Title
和其他字段)并根据 [=15 选中复选框=].如果为真,则应选中节点中的复选框。
澄清有关方法的信息
您说:
I want to iterate over treeView nodes and get the TaskClassObject for each node and access its fields (TaskClassObject.task_Title and other fields) and checkbox gets checked based on TaskClassObject.task_Is_Completed. if its true, checkbox in node should be checked.
然而,那不是你真正想做的。您不想尝试遍历事物并担心像 TreeView 这样的虚拟化控件中的节点和东西。
GUI 编程是 event-driven,您要做的是响应事件并根据事件更新关联的模型状态。
你真正需要问(并且想要)的是:
How do I bind the selected value from a CheckBoxTreeItem to my model class, so that when a CheckBox in an associated CheckBoxTreeCell is selected or unselected my associated property in my model class is updated to reflect the new value?
(我将您的问题标题更新为其中的一个子集,以便其他人遇到相同问题时可以轻松找到它)。
如何实现需要的绑定
CheckBoxTreeItem
和 CheckBoxTreeCell
class 相当 high-level class 被设置为响应适当的事件并根据更新模型状态在他们之上。您需要利用它们提供的 high-level 接口来使它们真正可用。要为您的示例执行此操作,您需要:
使您的模型 class(
Task
)使用并公开 JavaFX 属性,以便 UI 可以根据 属性 自动更新UI 的值和更新可以自动更新 属性 值。将任务实例中的
complete
属性 绑定到关联的CheckBoxTreeItem
。这是通过 bi-directional 绑定完成的,因此Task
中完整 属性 的更新将更新 UI 和 vice-versa.[=24 中的选择状态=]task.completeProperty().bindBidirectional(treeItem.selectedProperty());
向
CheckBoxTreeCell
提供选定的 属性 绑定回调,以将其与您的CheckBoxTreeItem
相关联。这实际上通常默认发生,但是,如果您还想要一个 StringConverter(在这种情况下您这样做),那么据我所知,您需要手动提供一个。为
CheckBoxTreeCell
提供一个 StringConverter,以便它能够以清晰的方式显示您的Task
。
示例代码
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.CheckBoxTreeItem;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.CheckBoxTreeCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.StringConverter;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
public class App extends Application {
public static void main(String[] args) {
launch();
}
@Override
public void start(Stage stage) {
Task task = createTask();
CheckBoxTreeItem<Task> treeItem = new CheckBoxTreeItem<>(task);
task.completeProperty().bindBidirectional(treeItem.selectedProperty());
TreeView<Task> treeView = new TreeView<>();
attachTaskCellFactory(treeView);
treeView.setRoot(treeItem);
StackPane layout = new StackPane(treeView);
Scene Scene = new Scene(layout);
stage.setScene(Scene);
stage.show();
}
private void attachTaskCellFactory(TreeView<Task> treeView) {
Callback<TreeItem<Task>, ObservableValue<Boolean>> getSelectedProperty =
item -> {
if (item instanceof CheckBoxTreeItem<?>) {
return ((CheckBoxTreeItem<?>)item).selectedProperty();
}
return null;
};
StringConverter<TreeItem<Task>> treeItemStringConverter = new StringConverter<>() {
@Override
public String toString(TreeItem<Task> treeItem) {
return treeItem == null || treeItem.getValue() == null || treeItem.getValue().getTitle() == null
? ""
: treeItem.getValue().getTitle();
}
@Override
public TreeItem<Task> fromString(String string) {
// not actually used, only provided to allow a concrete instance to be created.
return null;
}
};
treeView.setCellFactory(
CheckBoxTreeCell.forTreeView(
getSelectedProperty,
treeItemStringConverter
)
);
}
private Task createTask() {
Task task = new Task(
"Buy a Car",
Duration.of(7, ChronoUnit.DAYS),
Duration.of(2, ChronoUnit.DAYS),
false
);
task.completeProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("Task complete: " + newValue);
});
return task;
}
public class Task {
final private StringProperty title = new SimpleStringProperty();
final private ObjectProperty<Duration> duration = new SimpleObjectProperty<>();
final private ObjectProperty<Duration> remainingDays = new SimpleObjectProperty<>();
final private BooleanProperty complete = new SimpleBooleanProperty();
public Task(String title, Duration duration, Duration remainingDays, Boolean complete) {
setTitle(title);
setDuration(duration);
setRemainingDays(remainingDays);
setComplete(complete);
}
public String getTitle() {
return title.get();
}
public StringProperty titleProperty() {
return title;
}
public void setTitle(String title) {
this.title.set(title);
}
public Duration getDuration() {
return duration.get();
}
public ObjectProperty<Duration> durationProperty() {
return duration;
}
public void setDuration(Duration duration) {
this.duration.set(duration);
}
public Duration getRemainingDays() {
return remainingDays.get();
}
public ObjectProperty<Duration> remainingDaysProperty() {
return remainingDays;
}
public void setRemainingDays(Duration remainingDays) {
this.remainingDays.set(remainingDays);
}
public boolean isComplete() {
return complete.get();
}
public BooleanProperty completeProperty() {
return complete;
}
public void setComplete(boolean complete) {
this.complete.set(complete);
}
}
}