Multi-Window Apps (Window အများ ပါတဲ့ app များ)

စာမေးပွဲ ခေါင်းစဉ် မဟုတ်ဘဲ IA အတွက် အသုံးဝင်တဲ့ tool ပါ: IA (IB သင်တန်းမှာ ကျောင်းသားတစ်ယောက်ချင်း ကိုယ်တိုင် တည်ဆောက်ရပြီး ကျောင်းက ဆရာက အမှတ်ပေးတဲ့ အပိုင်း) အတွက် တည်ဆောက်တဲ့ product (program ဒါမှမဟုတ် app) တော်တော်များများမှာ screen တစ်ခုထက် ပိုပါပါတယ်။ ဥပမာ main screen တစ်ခု၊ record (အချက်အလက် အများကြီး စုထားတဲ့ ဒေတာ တစ်ခု) အသစ် ထည့်ဖို့ ဒါမှမဟုတ် ပြင်ဖို့ form တစ်ခု၊ record တစ်ခုရဲ့ အသေးစိတ်ကို ပြတဲ့ window တစ်ခု စသဖြင့်ပါ။ ဒီစာမျက်နှာက JavaFX (Java နဲ့ desktop app တည်ဆောက်တဲ့ library) မှာ screen အများကို ဘယ်လို စီမံမလဲ၊ screen တွေကြား ဒေတာကို ဘယ်လို မျှသုံးမလဲ ဆိုတာ ပြပါတယ်။ JavaFX က စာမေးပွဲမှာ မေးတဲ့ အကြောင်းအရာ မဟုတ်ပါဘူး။ IA မှာလည်း screen ဒါမှမဟုတ် window ဘယ်နှစ်ခု ပါရမယ်လို့ မသတ်မှတ်ထားပါဘူး။ ဒါပေမယ့် “Window အများအတွက် method တစ်ခုတည်း” အပိုင်းထဲက openWindow method (သီးခြား အလုပ်တစ်ခု လုပ်ပေးတဲ့ code အပိုင်း) က စာမေးပွဲမှာ မေးတဲ့ အယူအဆ တစ်ခုကို လေ့ကျင့်ပေးပါတယ်: parameter (method ကို ပေးလိုက်တဲ့ တန်ဖိုးကို လက်ခံတဲ့ variable) ပါတဲ့ function တွေနဲ့ modularization (သီးခြား အပိုင်းငယ်တွေ ခွဲရေးခြင်း) ရဲ့ အကျိုးကျေးဇူးတွေပါ (IB syllabus ရဲ့ B2.3.4)။ Python နဲ့ ရေးနေသလား။ ဒီစာမျက်နှာရဲ့ tkinter version က Multi-Window Apps with tkinter ပါ။


ခြုံငုံ မိတ်ဆက်

IA product တော်တော်များများမှာ screen အများ ပါပါတယ်၊ ဥပမာ:

  • List ဒါမှမဟုတ် အကျဉ်းချုပ်ကို ပြတဲ့ main screen (အဓိက screen)
  • Record အသစ် ထည့်ဖို့ ဒါမှမဟုတ် ရှိပြီးသား record ကို ပြင်ဖို့ add or edit form (ထည့်တဲ့ ဒါမှမဟုတ် ပြင်တဲ့ form)
  • Record တစ်ခုရဲ့ အသေးစိတ်ကို ပြတဲ့ details window
  • Settings window ဒါမှမဟုတ် About window

JavaFX Basics (JavaFX အခြေခံများ) မှာ လေ့လာခဲ့တဲ့ အလွှာ သုံးလွှာကို ပြန်သတိရပါ: Stage က window ဖြစ်ပါတယ်၊ Scene က window ထဲမှာ ပြတဲ့ အရာအားလုံးကို ထိန်းထားပါတယ်၊ scene ရဲ့ root (Parent တစ်ခု၊ ဥပမာ FXMLLoader က FXML file (screen ပုံစံကို ဖော်ပြတဲ့ file) ကနေ တည်ဆောက်ပေးတဲ့ layout (control တွေကို နေရာချ စီပေးတဲ့ အကွက်)) က control (button၊ text box စတဲ့ user သုံးတဲ့ အစိတ်အပိုင်း) တွေကို ထိန်းထားပါတယ်။

ဒီစာမျက်နှာမှာ screen တစ်ခုထက် ပိုပြနည်း နှစ်မျိုးကို လေ့လာပါမယ်: window တစ်ခုတည်းထဲမှာ scene ကို လဲပြတာ (နည်းလမ်း 1)၊ ဒါမှမဟုတ် ကိုယ်ပိုင် Stage တစ်ခုစီ ရှိတဲ့ window အပိုတွေ ဖွင့်တာ (နည်းလမ်း 2) ပါ။ ဘယ်နည်းကို သုံးသုံး ဒေတာ တစ်ခုတည်းနဲ့ အလုပ်လုပ်တဲ့ screen တွေက အဲဒီ ဒေတာကို မျှသုံးနိုင်ဖို့ နည်းလမ်း လိုပါတယ်၊ အဲဒါကို နည်းလမ်း 3 မှာ ရှင်းပြထားပါတယ်။ ကိုယ့် product နဲ့ ကိုက်တာကို ရွေးပါ။ စနစ်တကျ စီထားတဲ့ screen တစ်ခုတည်း ပါတဲ့ product လည်း ရပါတယ်။


နည်းလမ်း 1: Scene Swapping (window တစ်ခုတည်းထဲမှာ screen ကို လဲပြခြင်း)

အရိုးရှင်းဆုံး နည်းက window တစ်ခုတည်းကို ထားပြီး အဲဒီ window ထဲမှာ ပြတာကို ပြောင်းတာပါ။ Screen တစ်ခုစီမှာ ကိုယ်ပိုင် FXML file နဲ့ ကိုယ်ပိုင် controller (အဲဒီ screen ရဲ့ button တွေနဲ့ တခြား control တွေကို ကိုင်တွယ်တဲ့ Java class) ရှိပါတယ်။ Screen ပြောင်းဖို့ program က နောက် file ကို load လုပ်၊ Scene အသစ် တစ်ခုထဲ ထည့်ပြီး အဲဒီ Scene ကို အရင်က window ကိုပဲ ပေးပါတယ်။ ဒါကို scene swapping လို့ ခေါ်ပါတယ်။

အလုပ်လုပ်ပုံ

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

public class MyApp extends Application {
    private Stage primaryStage;

    @Override
    public void start(Stage stage) throws Exception {
        this.primaryStage = stage;
        showMainView();
        stage.setTitle("My App");
        stage.show();
    }

    public void showMainView() throws Exception {
        FXMLLoader loader = new FXMLLoader(
            getClass().getResource("main-view.fxml")
        );
        Parent root = loader.load();

        MainController controller = loader.getController();
        controller.setApp(this);  // pass a reference so the controller can switch views

        primaryStage.setScene(new Scene(root, 500, 400));
    }

    public void showAddView() throws Exception {
        FXMLLoader loader = new FXMLLoader(
            getClass().getResource("add-view.fxml")
        );
        Parent root = loader.load();

        AddController controller = loader.getController();
        controller.setApp(this);

        primaryStage.setScene(new Scene(root, 500, 400));
    }

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

Controller ကနေ scene ပြောင်းခြင်း

import javafx.fxml.FXML;

public class MainController {
    private MyApp app;

    public void setApp(MyApp app) { this.app = app; }

    @FXML
    private void handleAddButton() throws Exception {
        app.showAddView();
    }
}

AddController မှာလည်း setApp method အတူတူ ရှိဖို့ လိုပါတယ်၊ ဒါမှ သူ့ရဲ့ Back button က MyApp ကို main screen ပြန်ပြခိုင်းလို့ ရမှာပါ။ Controller ကို JavaFX ဖန်တီးထားတဲ့ MyApp object (class တစ်ခုကနေ ဖန်တီးထားတဲ့ instance) ကိုပဲ ပေးရပါမယ်။ Controller က new MyApp() နဲ့ ကိုယ်တိုင် ဖန်တီးရင် primaryStage က null (ဘာ object ကိုမှ မညွှန်းတဲ့ တန်ဖိုး) ဖြစ်နေတဲ့ ဒုတိယ object တစ်ခု ရသွားပြီး screen ပြောင်းတဲ့အခါ NullPointerException (null reference ကို သုံးမိလို့ ဖြစ်တဲ့ error) နဲ့ ရပ်သွားပါတယ်။

ဘယ်အချိန် သုံးမလဲ: တစ်ချိန်မှာ screen တစ်ခုပဲ မြင်ရတဲ့ ရိုးရှင်းတဲ့ app တွေမှာ သုံးပါ။

ကန့်သတ်ချက်: showMainView() ဒါမှမဟုတ် showAddView() ကို ခေါ်တိုင်း FXML file ကို ပြန် load လုပ်ပြီး control အသစ်တွေနဲ့ controller အသစ် တစ်ခုကို တည်ဆောက်ပါတယ်။ ဒါကြောင့် user က screen တစ်ခုမှာ ရိုက်ထည့်ထားတာတွေက အဲဒီ screen ကို ပြန်လာတဲ့အခါ ပျောက်သွားပါတယ်၊ Model (app ရဲ့ ဒေတာနဲ့ စည်းမျဉ်းတွေကို ထိန်းထားတဲ့ class တွေ) ထဲမှာ အရင် သိမ်းထားမှသာ ကျန်ပါတယ်။


နည်းလမ်း 2: Popup Windows (main window ရှေ့မှာ ပွင့်လာတဲ့ သီးခြား window များ)

Popup ဆိုတာ main window ရှေ့မှာ ပွင့်လာတဲ့ သီးခြား window (Stage အသစ် တစ်ခု) ပါ။ ဥပမာ form တစ်ခုကို ပြဖို့ သုံးပြီး main window ကလည်း screen ပေါ်မှာ ဆက်ရှိနေပါတယ်။

Popup window တစ်ခု ဖန်တီးခြင်း

Main screen ရဲ့ controller ထဲမှာ ဒီလို ရေးပါ (import တွေ ပါတဲ့ class (object တွေ ဖန်တီးဖို့ ပုံစံခွက်) အပြည့်အစုံကို အောက်က ဥပမာ အပြည့်အစုံ မှာ ကြည့်နိုင်ပါတယ်):

@FXML
private void handleAdd() throws Exception {
    FXMLLoader loader = new FXMLLoader(
        getClass().getResource("add-student-view.fxml")
    );
    Parent root = loader.load();

    AddStudentController addCtrl = loader.getController();
    addCtrl.setModel(model);

    Stage popup = new Stage();
    popup.setTitle("Add Student");
    popup.setScene(new Scene(root, 350, 250));
    popup.initModality(Modality.APPLICATION_MODAL);  // blocks the app's other windows
    popup.showAndWait();

    // After the popup closes, refresh the main display
    refreshDisplay();
}

အဓိက အယူအဆများ

Modality.APPLICATION_MODAL: Window တစ်ခုရဲ့ modality (ပွင့်နေစဉ် တခြား ဘယ် window တွေကို မသုံးနိုင်အောင် တားထားမလဲ ဆိုတဲ့ setting) က အဲဒီ window ပွင့်နေစဉ် တခြား ဘယ် window တွေကို တားထားမလဲ သတ်မှတ်ပါတယ်။ တခြား window တွေကို တားထားတဲ့ window ကို modal window လို့ ခေါ်ပါတယ်။ APPLICATION_MODAL နဲ့ဆိုရင် popup က မပိတ်မချင်း app ရဲ့ တခြား window တွေ (ဒီဥပမာမှာ main window) ကို တားထားပါတယ်၊ ဒါကြောင့် user က popup ကို အရင် ပြီးအောင် လုပ်ရမယ်၊ ဒါမှမဟုတ် cancel လုပ်ရပါမယ်။ ဒါပေမယ့် popup ကိုယ်တိုင် ပိုင်တဲ့ window တွေ (တိုက်ရိုက် ပိုင်တာဖြစ်ဖြစ်၊ တခြား window တစ်ခုကနေ တစ်ဆင့် ပိုင်တာဖြစ်ဖြစ်) ကိုတော့ မတားပါဘူး၊ ဥပမာ ပထမ popup ကို owner အဖြစ် ထားပြီး အဲဒီ popup ကနေ ဖွင့်တဲ့ ဒုတိယ popup မျိုးပါ။ ဒါက add form နဲ့ edit form တွေအတွက် သင့်တော်ပါတယ်။

initOwner နဲ့ Modality.WINDOW_MODAL: popup.initOwner(display.getScene().getWindow()) က display text area ပါတဲ့ main window ကို popup ရဲ့ owner (popup ကို ပိုင်တဲ့ window) ဖြစ်အောင် လုပ်ပါတယ်။ အဲဒီနောက် popup က main window ရဲ့ ရှေ့မှာ ဆက်ရှိနေပါတယ်။ WINDOW_MODAL နဲ့ဆိုရင် popup က သူ့ owner ကို တားထားပါတယ်၊ အဲဒီ owner ရဲ့ owner ကိုလည်း တားထားပါတယ်၊ ဒီလိုနဲ့ owner ကွင်းဆက်တစ်လျှောက် ကိုယ်ပိုင် owner မရှိတဲ့ ပထမဆုံး window အထိ တားထားပါတယ်။ အဲဒီ ကွင်းဆက် အပြင်က window တွေကိုတော့ ဆက်သုံးလို့ ရပါတယ်။ Owner မရှိရင် WINDOW_MODAL က ဘာကိုမှ မတားပါဘူး။ initModality နဲ့ initOwner ကို popup မပြခင် ခေါ်ပါ။

showAndWait(): Window ကို ဖွင့်ပြီး window မပိတ်မချင်း ခေါ်တဲ့ code ကို ခဏ ရပ်ထားပါတယ် (ဒီဥပမာမှာ handleAdd() ရဲ့ ကျန်တဲ့ အပိုင်း)။ ဒါကြောင့် နောက် line က refreshDisplay() က popup ပိတ်ပြီးမှပဲ run ပါတယ်။ ဒီ method က ကိုယ့် code ဖန်တီးထားတဲ့ Stage မှာပဲ အလုပ်လုပ်ပါတယ်၊ primary stage (JavaFX က start() ကို ပေးလိုက်တဲ့ window) မှာ အလုပ်မလုပ်ပါဘူး။

show(): Window ကို ဖွင့်ပြီး window ပွင့်နေတုန်းမှာပဲ ခေါ်တဲ့ code ကို ချက်ချင်း ဆက် run ခွင့်ပေးပါတယ်။ Help window လို main window နဲ့ အတူ user ဖွင့်ထားမယ့် window အတွက် သုံးပါ။

ခဏ ရပ်ထားတာနဲ့ တားထားတာက သီးခြား အလုပ် နှစ်ခုပါ။ showAndWait() က ကိုယ့် code ကို ခဏ ရပ်ထားပါတယ်။ Modality ကတော့ user က တခြား window တွေကို click မလုပ်နိုင်အောင် တားထားပါတယ်။ Add form ဒါမှမဟုတ် edit form တစ်ခုမှာ များသောအားဖြင့် နှစ်ခုလုံး လိုပါတယ်။

Popup ရဲ့ controller

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

public class AddStudentController {
    private StudentList model;

    @FXML private TextField nameField;
    @FXML private TextField gradeField;
    @FXML private Label status;

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

    @FXML
    private void handleSave() {
        String name = nameField.getText().trim();
        String gradeText = gradeField.getText().trim();

        if (name.isEmpty()) {
            status.setText("Name cannot be empty.");
            return;
        }

        int grade;
        try {
            grade = Integer.parseInt(gradeText);
        } catch (NumberFormatException e) {
            status.setText("Grade must be a number.");
            return;
        }

        try {
            model.addStudent(new Student(name, grade));
            // Close this window
            nameField.getScene().getWindow().hide();
        } catch (IllegalArgumentException e) {
            status.setText(e.getMessage());
        }
    }

    @FXML
    private void handleCancel() {
        nameField.getScene().getWindow().hide();
    }
}

Popup ကို သူ့ controller ကနေ ပိတ်ခြင်း: Controller က screen ပေါ်မှာ မရှိလို့ ကိုယ်ပိုင် window မရှိပါဘူး။ Screen ပေါ်က control တစ်ခုက သူ့ရဲ့ scene ကို (getScene()) ပေးနိုင်ပြီး scene က သူ့ရဲ့ window ကို (getWindow()) ပေးနိုင်ပါတယ်။ ဒါကြောင့် nameField.getScene().getWindow().hide() က nameField ပါတဲ့ window ကို ပိတ်ပါတယ်။ Popup ကို ပိတ်လိုက်တာက showAndWait() ရဲ့ ခဏ ရပ်ထားမှုကိုလည်း အဆုံးသတ်ပါတယ်: popup ရဲ့ handler (button နှိပ်တာလို event ဖြစ်တဲ့အခါ run တဲ့ code) ပြီးသွားတာနဲ့ main controller ထဲက showAndWait() နောက်က code တွေ run ပါတယ်။

ဘယ်အချိန် သုံးမလဲ: User က form ဖြည့်နေစဉ် main screen ကိုလည်း မြင်ဖို့ လိုတဲ့ app တွေ၊ ဒါမှမဟုတ် dialog box (user ဆီက တစ်ခုခုကို မေးတဲ့ window အသေး) လို form တစ်ခုကို main screen ရဲ့ ရှေ့မှာ ဖွင့်ရမယ့် app တွေမှာ သုံးပါ။


နည်းလမ်း 3: Shared Model (controller တွေ မျှသုံးတဲ့ Model တစ်ခုတည်း)

အပေါ်က နည်းလမ်း နှစ်ခုလုံးမှာ controller တွေကြား ဒေတာ မျှသုံးဖို့ နည်းလမ်း လိုပါတယ်။ ရှင်းလင်းတဲ့ နည်းတစ်ခုက shared Model ပါ: လိုအပ်တဲ့ controller တိုင်းကို Model object တစ်ခုတည်း ကိုပဲ ပေးပါ။

ပုံစံ

flowchart TD
    A["Application class<br>creates the Model once"] -- "passes model to" --> B["MainController"]
    B -- "passes the same model to" --> C["AddStudentController"]
    B -- "model" --> M[("StudentList<br>(the same object)")]
    C -- "model" --> M

ပုံထဲက စာတွေက ဒီလို ဆိုလိုပါတယ်: Application class က Model ကို တစ်ကြိမ်ပဲ ဖန်တီးပါတယ် (creates the Model once)၊ ပြီးရင် model ကို MainController ဆီ ပေးပါတယ် (passes model to)၊ MainController က အဲဒီ model တစ်ခုတည်းကို AddStudentController ဆီ ဆက်ပေးပါတယ် (passes the same model to)။ Controller နှစ်ခုလုံးက StudentList object တစ်ခုတည်း (the same object) ကို သုံးပါတယ်။

// In the Application class:
StudentList model = new StudentList();  // one StudentList for the whole app

// Pass it to the main controller:
MainController mainCtrl = loader.getController();
mainCtrl.setModel(model);

// In MainController, when opening a popup:
AddStudentController addCtrl = popupLoader.getController();
addCtrl.setModel(model);  // the same StudentList object

Controller နှစ်ခုလုံးက StudentList object တစ်ခုတည်း ကို ညွှန်းနေလို့ (Object References မှာလို object တစ်ခုကို ညွှန်းတဲ့ reference (object ရှိရာ memory နေရာကို ညွှန်းတဲ့ တန်ဖိုး) နှစ်ခုပါ)၊ popup ထဲမှာ ထည့်လိုက်တဲ့ ကျောင်းသားက popup ပိတ်ပြီးနောက် main controller က display ကို refresh လုပ်တဲ့အခါ ဖတ်တဲ့ list ထဲမှာ ရှိနေပြီးသား ဖြစ်ပါတယ်။

Controller တစ်ခုစီထဲမှာ Model အသစ် မဖန်တီးပါနဲ့။ ဒီလို လုပ်ရင် controller တစ်ခုစီမှာ သီးခြား ဒေတာ ရှိသွားပြီး တစ်ခုမှာ ပြောင်းလိုက်တာ နောက်တစ်ခုမှာ ပေါ်မလာပါဘူး။

နောက်ထပ် နည်းတစ်ခု: app တစ်ခုလုံးအတွက် မျှသုံးတဲ့ object တစ်ခု။ တချို့ app တွေက screen တိုင်း လိုတဲ့ ဒေတာကို singleton (instance တစ်ခုတည်းပဲ ရှိတဲ့ class ပုံစံ) ထဲမှာ သိမ်းထားပါတယ်။ Singleton ဆိုတာ ကိုယ့်ရဲ့ object တစ်ခုပဲ ဖန်တီးခွင့်ပေးတဲ့ class ပါ။ ဒါက HL syllabus ထဲက design pattern (ထပ်ကြုံရတဲ့ ဒီဇိုင်း ပြဿနာအတွက် ဖြေရှင်းပုံစံ) တွေထဲက တစ်ခုပါ (B3.2.5၊ HL ဆိုတာ IB သင်တန်းမှာ ပိုနက်နက်ရှိုင်းရှိုင်း လေ့လာရတဲ့ Higher Level အဆင့်ပါ)။ Terminology reference (OOP ဝေါဟာရ ကိုးကား စာမျက်နှာ) ကိုလည်း ကြည့်ပါ။ ဘယ် controller ကမဆို AppData.getInstance() လို ခေါ်မှုနဲ့ အဲဒီ object ကို ယူသုံးနိုင်လို့ ဘာမှ ပေးပို့စရာ မလိုပါဘူး။ ဒါပေမယ့် ဘယ် class ကမဆို ဒေတာကို ယူပြီး ပြောင်းနိုင်တာကြောင့် အပြောင်းအလဲ တစ်ခု ဘယ်ကလာလဲ ဆိုတာ မြင်ရ ခက်သွားပါတယ်။ JavaFX Cookbook ရဲ့ Share data across the whole app recipe (ချက်ချင်း ယူသုံးလို့ရတဲ့ code နမူနာ) မှာ ဥပမာ တစ်ခု ပြထားပါတယ်။ အပေါ်မှာ ပြခဲ့သလို Model ကို လိုတဲ့ controller တွေဆီ ပေးတာက ဘယ် screen က ဘယ် ဒေတာကို သုံးလဲ ဆိုတာ ရှင်းရှင်းလင်းလင်း မြင်ရစေပါတယ်။


Popup ကို ဒေတာ ပေးပို့ခြင်း

တခါတလေ popup က record တစ်ခုကို သီးသန့် လိုပါတယ်: ဥပမာ ကျောင်းသား တစ်ယောက်ရဲ့ အသေးစိတ်တွေ ဖြည့်ပြီးသားနဲ့ ပွင့်လာတဲ့ Edit Student window မျိုးပါ။ ဒီ version မှာ main window မှာလည်း user က ပြင်ချင်တဲ့ ကျောင်းသားရဲ့ နာမည်ကို ရိုက်ထည့်ဖို့ text box တစ်ခု (nameField) ပါပါတယ်။

@FXML
private void handleEdit() throws Exception {
    Student selected = model.findByName(nameField.getText().trim());
    if (selected == null) {
        status.setText("No student with that name.");
        return;
    }

    FXMLLoader loader = new FXMLLoader(
        getClass().getResource("edit-student-view.fxml")
    );
    Parent root = loader.load();

    EditStudentController editCtrl = loader.getController();
    editCtrl.setStudent(selected);  // pass in the student to edit

    Stage popup = new Stage();
    popup.setTitle("Edit Student");
    popup.setScene(new Scene(root, 350, 250));
    popup.initModality(Modality.APPLICATION_MODAL);
    popup.showAndWait();

    refreshDisplay();
}
// In EditStudentController:
public void setStudent(Student student) {
    this.student = student;
    nameField.setText(student.getName());
    gradeField.setText(String.valueOf(student.getGrade()));
}

setStudent() က load() ပြန်လာပြီးမှ run တာကြောင့် box တွေထဲ စာ ထည့်နိုင်ပါတယ်။ အဲဒီအချိန်မှာ loader က @FXML field (object ထဲက အချက်အလက် အကွက်) တစ်ခုစီကို သက်ဆိုင်ရာ control နဲ့ ချိတ်ပြီးသား ဖြစ်နေလို့ပါ (What Happens When FXMLLoader Runs ကို ကြည့်ပါ)။ Popup ကို StudentList ထဲမှာ ရှိတဲ့ Student object အတိုင်းကိုပဲ ပေးထားလို့ popup ထဲမှာ ပြင်လိုက်တာက list ထဲက ကျောင်းသားကို ပြင်လိုက်တာ ဖြစ်ပြီး main window က refresh လုပ်ရုံပဲ လိုပါတယ်။

Popup ကနေ အဖြေ ပြန်ယူခြင်း

တခါတလေ user က popup ထဲမှာ ဘာလုပ်ခဲ့လဲ (ဥပမာ Save ကို နှိပ်ခဲ့လား၊ Cancel ကို နှိပ်ခဲ့လား) ဆိုတာ main window က သိဖို့ လိုပါတယ်။ Popup ရဲ့ controller မှာ Save button က သတ်မှတ်ပေးတဲ့ field တစ်ခုနဲ့ အဲဒီ field ကို ပြန်ပေးတဲ့ getter (private attribute ရဲ့ တန်ဖိုးကို ပြန်ပေးတဲ့ method) တစ်ခု ထည့်ပါ။ Main controller က popup ရဲ့ controller ကို editCtrl ထဲမှာ ဆက်ကိုင်ထားလို့ showAndWait() ပြန်လာပြီးတာနဲ့ မေးကြည့်နိုင်ပါတယ်:

popup.showAndWait();

// isSaved() returns a boolean field that the popup's Save handler sets to true
if (editCtrl.isSaved()) {
    status.setText("Changes saved.");
} else {
    status.setText("No changes made.");
}
refreshDisplay();

Window အများအတွက် method တစ်ခုတည်း

App မှာ window တွေ များလာတာနဲ့အမျှ window ဖွင့်တဲ့ code တွေ ထပ်နေလာပါတယ်။ ဒါက main controller ထဲက handler နှစ်ခုပါ၊ ဒေတာ မလိုတဲ့ Help window နဲ့ About window ကို ဖွင့်ပါတယ်:

@FXML
private void handleHelp() throws Exception {
    Parent root = FXMLLoader.load(getClass().getResource("help-view.fxml"));
    Stage stage = new Stage();
    stage.setTitle("Help");
    stage.setScene(new Scene(root));
    stage.show();
}

@FXML
private void handleAbout() throws Exception {
    Parent root = FXMLLoader.load(getClass().getResource("about-view.fxml"));
    Stage stage = new Stage();
    stage.setTitle("About");
    stage.setScene(new Scene(root));
    stage.show();
}

Method နှစ်ခုက file name နဲ့ title ကလွဲရင် အတူတူပါပဲ။ အဲဒီ တန်ဖိုး နှစ်ခုကို parameter အဖြစ် လက်ခံတဲ့ method တစ်ခုက နှစ်ခုလုံးကို အစားထိုးနိုင်ပါတယ်။ ဒါက B2.3.4 ထဲက အယူအဆပါ: function ဆိုတာ မတူတဲ့ input (program ထဲ ထည့်ပေးတဲ့ ဒေတာ) တွေနဲ့ ခေါ်သုံးနိုင်ပြီး ပြန်သုံးလို့ရတဲ့ code အပိုင်း တစ်ခုပါ။

// Needs import java.io.IOException;
private void openWindow(String fxmlFile, String title) {
    try {
        Parent root = FXMLLoader.load(getClass().getResource(fxmlFile));
        Stage stage = new Stage();
        stage.setTitle(title);
        stage.setScene(new Scene(root));
        stage.show();
    } catch (IOException e) {
        status.setText("The " + title + " window could not be opened.");
    }
}

@FXML
private void handleHelp() {
    openWindow("help-view.fxml", "Help");
}

@FXML
private void handleAbout() {
    openWindow("about-view.fxml", "About");
}

ဒီလို ရေးတာရဲ့ အကျိုးကျေးဇူးတွေ (Methods မှာ လေ့လာခဲ့တဲ့ modularization ရဲ့ အကျိုးကျေးဇူးတွေ) ပါ:

  • Window အသစ် တစ်ခုအတွက် handler ထဲမှာ line ငါးကြောင်း မလိုတော့ဘဲ line တစ်ကြောင်းပဲ လိုပါတယ်။
  • Window အားလုံး ပွင့်ပုံကို ပြောင်းချင်ရင် (ဥပမာ window အားလုံးကို အရွယ်အစား တူအောင် လုပ်ချင်ရင်) နေရာ တစ်ခုတည်းမှာပဲ ပြင်ရပါတယ်။
  • Handler တစ်ခုစီက ဘာလုပ်လဲ ဆိုတာ line တစ်ကြောင်းတည်းနဲ့ ပြောပြပါတယ်။

openWindow က FXMLLoader.load ရဲ့ line တစ်ကြောင်းတည်း ပုံစံကို သုံးပါတယ်။ ဒီပုံစံက screen ကိုပဲ ပြန်ပေးလို့ window အသစ်ရဲ့ controller ကို မယူနိုင်ပါဘူး။ Add Student form လို ဒေတာ လိုတဲ့ window ကိုတော့ နည်းလမ်း 2 မှာလို အဆင့် နှစ်ဆင့်နဲ့ ဆက်ဖွင့်ရပါတယ် (FXMLLoader တစ်ခု ဖန်တီးပြီးမှ load() ကို ခေါ်ပါ)။ ပြီးရင် code က loader ဖန်တီးထားတဲ့ controller ကို ပြန်ပေးတဲ့ getController() ကို ခေါ်ပြီး Model ကို အဲဒီ controller ဆီ ပေးနိုင်ပါတယ်။

catch က ဘာတွေကို ကိုင်တွယ်ပေးလဲ။ File ကို ဖတ်လို့ မရရင် ဒါမှမဟုတ် file ထဲမှာ အမှား ပါနေရင် (ဥပမာ controller မှာ မရှိတဲ့ handler နာမည်) load() က IOException ဆိုတဲ့ exception (program run နေစဉ် ဖြစ်တဲ့ အမှား) ကို throw (exception ဖြစ်ကြောင်း အချက်ပြ) လုပ်ပြီး catch က message တစ်ခု ပြပါတယ်။ File name မှားတာကတော့ မတူပါဘူး: getResource က file ကို ရှာမတွေ့လို့ null ကို ပြန်ပေးပြီး load() က NullPointerException: Location is required. နဲ့ ရပ်သွားပါတယ်။ NullPointerException က IOException မဟုတ်လို့ ဒီ catch က အဲဒါကို မကိုင်တွယ်ပါဘူး။ Window မပွင့်ရင် file name နဲ့ file ရှိတဲ့ နေရာကို စစ်ကြည့်ပါ။


Window ကို ပိတ်ခြင်း၊ ဖျောက်ခြင်းနဲ့ ပြန်ပြခြင်း

Window ရဲ့ Stage ကို ယူခြင်း။ Controller က သူ့ control တစ်ခုခုကနေ တစ်ဆင့် ကိုယ်ပိုင် window ကို ယူနိုင်ပါတယ်။ getScene().getWindow() က window ကို Window အဖြစ် ပြန်ပေးပါတယ်။ Stage method တွေကို သုံးချင်ရင် cast (type ပြောင်း) လုပ်ပါ (ရှေ့မှာ (Stage) ထည့်ပါ၊ ဒါက Java ကို Stage အဖြစ် သဘောထားဖို့ ပြောတာပါ):

Stage stage = (Stage) status.getScene().getWindow();

ဒါက handler ထဲမှာ အလုပ်လုပ်ပါတယ်။ initialize() ထဲမှာတော့ အလုပ်မလုပ်ပါဘူး၊ အဲဒီအချိန်မှာ screen က scene ထဲ မရောက်သေးလို့ getScene() က null ကို ပြန်ပေးနေသေးပါတယ် (The initialize() Method ကို ကြည့်ပါ)။

hide() နဲ့ close()။ Stage အတွက်ဆိုရင် နှစ်ခုလုံး အတူတူ လုပ်ပါတယ်: window ပျောက်သွားပေမယ့် Stage object က ဆက်ရှိနေပါတယ်။ Program က အဲဒီ object ကို reference နဲ့ ဆက်ကိုင်ထားရင် show() က user ရိုက်ထည့်ထားတာတွေ အပါအဝင် အရင်အတိုင်း ပြန်ပြပေးပါတယ်။ ဘာကမှ မညွှန်းတော့ရင်တော့ Java က အဲဒီ object ရဲ့ memory (မှတ်ဉာဏ်) ကို ပြန်ရှင်းနိုင်ပါတယ်။ Program တော်တော်များများက ပြန်ပြမယ့် window အတွက် hide() ကို၊ သုံးလို့ ပြီးသွားတဲ့ window အတွက် close() ကို သုံးကြပါတယ်၊ ဒါမှ code က ဘာကို ဆိုလိုလဲ ရှင်းရှင်း ပြောနိုင်မှာပါ။

Main window ကို ပြန်ပြခြင်း။ တခြား window တစ်ခု ပွင့်နေစဉ် main window ကို ဖျောက်ထားပြီး အဲဒီ window ပိတ်တဲ့အခါ ပြန်ပြနိုင်ပါတယ်။ ဒီမှာ helpStage က အပေါ်က openWindow မှာလို ပြင်ဆင်ထားတဲ့ window အသစ်ပါ:

Stage mainStage = (Stage) status.getScene().getWindow();
helpStage.setOnHidden(event -> mainStage.show());  // runs when the Help window closes
helpStage.show();
mainStage.hide();

setOnHidden က window ကို ဖျောက်လိုက်တဲ့အခါ run မယ့် code ကို သတ်မှတ်ပေးပါတယ်။ Window ကို ဘယ်လို ပိတ်ပိတ် (close button နဲ့ဖြစ်ဖြစ်၊ close() နဲ့ဖြစ်ဖြစ်၊ hide() နဲ့ဖြစ်ဖြစ်) အဲဒီ code က run ပါတယ်။ Window အသစ် screen ပေါ် ရောက်ပြီးမှ main window ကို ဖျောက်တာကြောင့် window အသစ်က load မလုပ်နိုင်ရင် main window က ဆက်မြင်ရနေပါတယ်။ event -> mainStage.show() က lambda ပါ၊ JavaFX Basics မှာလို နာမည်မရှိတဲ့ method အတိုလေး တစ်ခုပါ။ Lambda တွေက စာမေးပွဲမှာ မေးတဲ့ အကြောင်းအရာ မဟုတ်ပါဘူး။

App ကို ပိတ်ခြင်း။ Platform.exit() (javafx.application.Platform ထဲကပါ) က app တစ်ခုလုံးကို ရပ်ပြီး Application class မှာ stop() method (app ရပ်ချိန်မှာ run မယ့် code) ရှိရင် အဲဒါကို run ပါတယ်။ close() ကတော့ window တစ်ခုကိုပဲ ပိတ်ပါတယ်။ Program က Platform.setImplicitExit(false) ကို မခေါ်ထားရင် နောက်ဆုံး ပွင့်နေတဲ့ window ပိတ်သွားတဲ့အခါ JavaFX က app ကို ရပ်ပါတယ်။ Cookbook ရဲ့ Quit the app cleanly ကို ကြည့်ပါ။


ဥပမာ အပြည့်အစုံ: Add popup ပါတဲ့ Student Manager

Student နဲ့ StudentList က MVC Architecture (app ကို Model၊ View၊ Controller ဆိုပြီး အပိုင်းသုံးပိုင်း ခွဲရေးတဲ့ ပုံစံ) ထဲက Model class တွေ ဖြစ်ပြီး AddStudentController က နည်းလမ်း 2 ထဲက popup ရဲ့ controller ပါ။ File အားလုံးကို folder တစ်ခုတည်းထဲမှာ ထားပါ။

StudentApp.java

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

public class StudentApp extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        StudentList model = new StudentList();

        FXMLLoader loader = new FXMLLoader(
            getClass().getResource("main-view.fxml")
        );
        Parent root = loader.load();

        MainController controller = loader.getController();
        controller.setModel(model);

        stage.setTitle("Student Manager");
        stage.setScene(new Scene(root, 500, 450));
        stage.show();
    }

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

MainController.java

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.stage.Modality;
import javafx.stage.Stage;

public class MainController {
    private StudentList model;

    @FXML private TextArea display;
    @FXML private Label status;

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

    @FXML
    private void handleAdd() throws Exception {
        FXMLLoader loader = new FXMLLoader(
            getClass().getResource("add-student-view.fxml")
        );
        Parent root = loader.load();

        AddStudentController addCtrl = loader.getController();
        addCtrl.setModel(model);

        Stage popup = new Stage();
        popup.setTitle("Add Student");
        popup.setScene(new Scene(root, 350, 250));
        popup.initModality(Modality.APPLICATION_MODAL);
        popup.showAndWait();

        refreshDisplay();
        status.setText("Students: " + model.size());
    }

    private void refreshDisplay() {
        display.clear();
        for (int i = 0; i < model.getAll().size(); i++) {
            display.appendText((i + 1) + ". " + model.getAll().get(i) + "\n");
        }
    }
}

main-view.fxml

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

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

<BorderPane xmlns:fx="http://javafx.com/fxml"
            fx:controller="MainController">

    <padding><Insets top="10" right="10" bottom="10" left="10"/></padding>

    <center>
        <TextArea fx:id="display" editable="false"/>
    </center>

    <bottom>
        <HBox spacing="10">
            <padding><Insets top="10"/></padding>
            <Button text="Add Student" onAction="#handleAdd"/>
            <Label fx:id="status" text="Ready"/>
        </HBox>
    </bottom>

</BorderPane>

add-student-view.fxml

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

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

<VBox xmlns:fx="http://javafx.com/fxml"
      fx:controller="AddStudentController" spacing="10">

    <padding><Insets top="15" right="15" bottom="15" left="15"/></padding>

    <TextField fx:id="nameField" promptText="Student name"/>
    <TextField fx:id="gradeField" promptText="Grade (0-100)"/>
    <HBox spacing="10">
        <Button text="Save" onAction="#handleSave"/>
        <Button text="Cancel" onAction="#handleCancel"/>
    </HBox>
    <Label fx:id="status"/>

</VBox>

Quick Check (အမြန် စစ်ဆေးမေးခွန်း)

Q1. popup.initModality(Modality.APPLICATION_MODAL) က ဘာလုပ်သလဲ။

Q2. Popup အတွက် show() အစား showAndWait() ကို ဘာကြောင့် သုံးသလဲ။

Q3. ဒီစာမျက်နှာမှာ main controller နဲ့ popup ရဲ့ controller က ကျောင်းသား ဒေတာကို ဘယ်လို မျှသုံးသလဲ။

Q4. AddStudentController ထဲမှာ main window ဆက်အလုပ်လုပ်နေအောင် popup ကိုပဲ ပိတ်ပေးတဲ့ line က ဘယ်ဟာလဲ။

Q5. Popup window (Stage အများ) ထက် scene swapping (Stage တစ်ခုတည်း) ကို ဘယ်အချိန်မှာ ပိုသုံးသင့်သလဲ။


Code Completion (code ကွက်လပ် ဖြည့်ခြင်း)

Main controller ကနေ popup window ဖွင့်ပေးတဲ့ code ကို ဖြည့်ပါ။

ကွက်လပ် ဖြည့်ပါ: ပြဇာတ်အသင်း တစ်ခုရဲ့ planner app မှာ Add Rehearsal (ပြဇာတ် အစမ်းလေ့ကျင့်ချိန် ထည့်ရန်) button တစ်ခု ပါပါတယ်။ Add Rehearsal popup ကို ဖွင့်ပြီး planner ရဲ့ model ကို popup ဆီ ပေးတဲ့ အဲဒီ button ရဲ့ handler ကို ဖြည့်ပါ။ Planner ရဲ့ list ကို popup ပိတ်ပြီးမှပဲ refresh လုပ်ရပါမယ်။

@FXML
private void handleAdd() throws Exception {
    FXMLLoader loader = new FXMLLoader(
        getClass().getResource("add-rehearsal-view.fxml")
    );
    Parent root = loader.;

    AddRehearsalController ctrl = loader.;
    ctrl.setModel(model);

    Stage popup = new ;
    popup.setScene(new Scene(root, 350, 250));
    popup.initModality(Modality.APPLICATION_MODAL);
    popup.;

    refreshDisplay();
}

Spot the Error (အမှား ရှာခြင်း)

ဒီ code က တီးဝိုင်း တစ်ခုရဲ့ set list (တီးမယ့် သီချင်း စာရင်း) ထဲကို သီချင်း ထည့်ဖို့ popup တစ်ခု ဖွင့်ပါတယ်၊ ဒါပေမယ့် popup ထဲမှာ ထည့်လိုက်တဲ့ သီချင်းတွေက main window ရဲ့ list ထဲမှာ မပေါ်ပါဘူး။

အမှား ရှာပါ: Popup ပိတ်ပြီးနောက် popup ထဲမှာ ထည့်ခဲ့တဲ့ သီချင်းတွေက main window ရဲ့ list ထဲမှာ မပေါ်ပါဘူး။

1FXMLLoader loader = new FXMLLoader(getClass().getResource("add-song-view.fxml")); 2Parent root = loader.load(); 3AddSongController ctrl = loader.getController(); 4ctrl.setModel(new SetList()); 5Stage popup = new Stage(); 6popup.setScene(new Scene(root)); 7popup.initModality(Modality.APPLICATION_MODAL); 8popup.showAndWait(); 9refreshDisplay();

Line 4 အတွက် မှန်ကန်တဲ့ ပြင်နည်းကို ရွေးပါ:


Practice Exercises (လေ့ကျင့်ခန်းများ)

(4 marks) စတာတွေက လေ့ကျင့်ခန်း တစ်ခုစီအတွက် ရနိုင်တဲ့ အမှတ်ပါ။ IB စာမေးပွဲ မေးခွန်းတွေမှာ အမှတ်ကို ဒီလို ပြလေ့ ရှိပါတယ်။

Core (အခြေခံ အဆင့်)

  1. Message ပြတဲ့ popup (4 marks): Show Message button ပါတဲ့ main window တစ်ခုကို Construct (ကိုယ်တိုင် တည်ဆောက်ပါ)။ အဲဒီ button ကို နှိပ်ရင် modal popup တစ်ခု ပွင့်ရပါမယ်: ကိုယ်ပိုင် FXML file ထဲက screen ကို ပြတဲ့ Stage အသစ် တစ်ခုဖြစ်ပြီး “Hello from the popup.” လို့ ရေးထားတဲ့ label တစ်ခုနဲ့ popup ကို ပိတ်ပေးတဲ့ Close button တစ်ခု ပါရပါမယ်။ Popup ပွင့်နေစဉ် user က main window ကို မသုံးနိုင်ရပါဘူး။

  2. Scene swap (4 marks): Window တစ်ခုတည်းထဲမှာ ပြတဲ့ screen နှစ်ခုပါ app တစ်ခုကို Construct လုပ်ပါ: နှုတ်ဆက်စာနဲ့ Go to Settings button ပါတဲ့ Home screen တစ်ခု၊ user ရဲ့ နာမည် ရိုက်ထည့်ဖို့ text box နဲ့ Back to Home button ပါတဲ့ Settings screen တစ်ခုပါ။ Button တစ်ခုခုကို နှိပ်ရင် Stage တစ်ခုတည်းထဲမှာ screen ကို အစားထိုးရပါမယ်။ User က Back to Home ကို နှိပ်တဲ့အခါ Home screen က “Welcome, “ နောက်မှာ Settings screen မှာ ရိုက်ထည့်ခဲ့တဲ့ နာမည်ကို ဆက်ပြရပါမယ်၊ box က ဗလာ ဖြစ်နေရင် “Welcome” ကိုပဲ ပြရပါမယ်။

Extension (တိုးချဲ့ အဆင့်)

  1. Edit popup (6 marks): Popup ကို ဒေတာ ပေးပို့ခြင်း အပိုင်းထဲက Edit Student feature ရဲ့ ကျန်တဲ့ အပိုင်းကို Construct လုပ်ပါ။ ဥပမာ အပြည့်အစုံ (Complete Example) ရဲ့ main window မှာ နာမည် box တစ်ခုနဲ့ Edit button တစ်ခု ထည့်ပါ။ edit-student-view.fxml နဲ့ EditStudentController ကို Construct လုပ်ပါ: Save က input ကို စစ်ပြီး Model ရဲ့ method တွေကနေ တစ်ဆင့် ကျောင်းသားကို update လုပ်ရပါမယ်၊ Model က တန်ဖိုး တစ်ခုကို လက်မခံရင် Model ရဲ့ message ကို ပြရပါမယ်၊ ပြီးရင် popup ကို ပိတ်ရပါမယ်။ Cancel ကတော့ ဘာမှ မပြောင်းဘဲ popup ကို ပိတ်ရပါမယ်။ Constructor (object ဖန်တီးချိန် attribute တွေကို စတင် သတ်မှတ်ပေးတဲ့ method) လုပ်သလိုပဲ ဗလာ နာမည်ကို လက်မခံတဲ့ setName method ကို Student ထဲ ထည့်ပါ။ Popup ပိတ်ပြီးနောက် main window ရဲ့ list မှာ ပြောင်းလဲမှုတွေ ပေါ်ရပါမယ်။

  2. မပိတ်ခင် အတည်ပြုခြင်း (6 marks): Student Manager ရဲ့ main window အတွက် ပိတ်ခါနီး စစ်ဆေးချက် တစ်ခုကို Construct လုပ်ပါ: user က window ရဲ့ close button (X) ကို နှိပ်တဲ့အခါ အတည်ပြုချက် Alert (JavaFX မှာ အသင့်ပါတဲ့ message window) တစ်ခုက OK နဲ့ Cancel button တွေနဲ့အတူ “Exit without saving?” (save မလုပ်ဘဲ ထွက်မှာလား) လို့ မေးရပါမယ်။ OK ကို နှိပ်ရင် window ကို ပိတ်ခွင့်ပေးပြီး Cancel ကို နှိပ်ရင် window ကို ဆက်ဖွင့်ထားရပါမယ်။ User က window ကို ပိတ်ဖို့ တောင်းဆိုတဲ့အခါ run မယ့် code ကို သတ်မှတ်ပေးတဲ့ Stage method setOnCloseRequest ကို ရှာဖတ်ကြည့်ပါ။

Challenge (စိန်ခေါ်မှု အဆင့်)

  1. ပစ္စည်းပျောက် ရုံး (8 marks): ကျောင်းတစ်ကျောင်းရဲ့ ပစ္စည်းပျောက် ရုံး (lost property office) အတွက် screen သုံးခုပါ FXML app တစ်ခုကို Construct လုပ်ပါ:
    • Main window: လာအပ်ထားတဲ့ ပစ္စည်း အားလုံးကို ပိုင်ရှင် လာယူပြီးပြီလား ဆိုတာနဲ့အတူ စာရင်းပြပြီး Log Item နဲ့ Item Details button တွေ ပါရပါမယ်။
    • Log Item popup (modal): ပစ္စည်းရဲ့ ဖော်ပြချက်နဲ့ ပစ္စည်းကို တွေ့ခဲ့တဲ့ နေရာကို လက်ခံပါတယ်။ ဖော်ပြချက် ဒါမှမဟုတ် နေရာ ဗလာ ဖြစ်နေရင် message တစ်ခု ပြပြီး လက်မခံရပါဘူး။
    • Item Details popup (modal): main window မှာ ရွေးထားတဲ့ ပစ္စည်းရဲ့ အချက်အလက် အားလုံးကို ပြပြီး Mark as Collected button ပါရပါမယ်။

    Screen သုံးခုလုံးက ပစ္စည်းတွေနဲ့ ပစ္စည်းဆိုင်ရာ စည်းမျဉ်းတွေကို ထိန်းထားတဲ့ LostProperty model object တစ်ခုတည်းနဲ့ အလုပ်လုပ်ရပါမယ်။ Popup ပိတ်တိုင်း main window ရဲ့ list က နောက်ဆုံး အခြေအနေအတိုင်း ဖြစ်နေရပါမယ်။

  2. Update မဖြစ်တဲ့ list ကို ရှင်းပြခြင်း (6 marks): Quiz night (အသင်းလိုက် ပြိုင်ဖြေတဲ့ ပဟေဠိ ညပွဲ) app တစ်ခုမှာ အသင်းတွေကို စာရင်းပြတဲ့ main window နဲ့ Add Team button ပါပါတယ်။ အဲဒီ button ရဲ့ handler က add-team-view.fxml ကို load လုပ်၊ main controller ရဲ့ TeamList ကို popup ရဲ့ controller ဆီ ပေး၊ popup ကို show() နဲ့ ဖွင့်ပြီးနောက် refreshDisplay() ကို ခေါ်ပါတယ်။ Popup က အသင်း အသစ်ကို အဲဒီ TeamList ထဲ ထည့်ပြီး ပိတ်သွားပါတယ်။ User က အသင်း တစ်သင်း ထည့်လိုက်ပေမယ့် main window ရဲ့ list မှာ မပေါ်ပါဘူး။ Add Team ကို နှိပ်လိုက်တဲ့ အချိန်ကစပြီး ဘာတွေ ဖြစ်သွားလဲ ဆိုတာကို အစဉ်လိုက် ဖော်ပြရင်း ဘာကြောင့်လဲ ဆိုတာ Explain (ရှင်းပြပါ)။ ပြီးရင် program ကို ပြင်နိုင်တဲ့ မတူတဲ့ နည်းလမ်း နှစ်ခုကို Describe (ဖော်ပြရှင်းပြပါ)။ Code ရေးဖို့ မလိုပါဘူး။

ဆက်စပ် ခေါင်းစဉ်များ

  • အရင် လေ့လာထားရမယ့် ခေါင်းစဉ်: JavaFX Basics (JavaFX အခြေခံများ): Stage၊ Scene၊ control တွေ
  • အရင် လေ့လာထားရမယ့် ခေါင်းစဉ်: MVC Architecture (MVC ဖွဲ့စည်းပုံ): shared Model ပုံစံ
  • အရင် လေ့လာထားရမယ့် ခေါင်းစဉ်: FXML and Controllers (FXML နဲ့ controller များ): FXMLLoader၊ @FXML၊ initialize()
  • အရင် လေ့လာထားရမယ့် ခေါင်းစဉ်: Object References (object reference များ): aliasing (variable နှစ်ခုက object တစ်ခုတည်းကို ညွှန်းနေခြင်း၊ shared Model ဆိုတာ object တစ်ခုကို ညွှန်းတဲ့ reference နှစ်ခုပါ)
  • ဆက်စပ် ခေါင်းစဉ်: Methods (method များ): parameter ပါတဲ့ function တွေနဲ့ modularization ရဲ့ အကျိုးကျေးဇူးများ (B2.3.4)
  • ဆက်စပ် ခေါင်းစဉ်: JavaFX Cookbook: App structure and navigation (app ဖွဲ့စည်းပုံနဲ့ screen တွေကြား သွားလာခြင်း): ဒုတိယ window ဖွင့်ခြင်း၊ screen တွေကြား အချက်အလက် ပေးပို့ခြင်းနဲ့ app ကို သေသေသပ်သပ် ပိတ်ခြင်းအတွက် recipe တွေ
  • နောက်ထပ် လေ့လာမယ့် ခေါင်းစဉ်: File I/O in Apps (app တွေထဲမှာ file ဖတ်ခြင်း၊ ရေးခြင်း): app ကို ပိတ်ပြီး ပြန်ဖွင့်တဲ့အခါမှာလည်း ဒေတာ ကျန်နေအောင် သိမ်းခြင်း
  • နောက်ထပ် လေ့လာမယ့် ခေါင်းစဉ်: Structuring Your IA (IA ကို ဖွဲ့စည်း တည်ဆောက်ခြင်း): အားလုံးကို ပေါင်းစပ်ခြင်း

© EduCS.me. ကွန်ပျူတာသိပ္ပံ ပညာရေးအတွက် အရင်းအမြစ်တွေ စုထားတဲ့ နေရာပါ။ EduCS is an independent educational resource. It is not affiliated with, or endorsed by, the International Baccalaureate Organization. (EduCS ဟာ သီးခြား ပညာရေး အရင်းအမြစ်တစ်ခုပါ။ International Baccalaureate Organization နဲ့ ဆက်စပ်မှု မရှိသလို သူတို့ရဲ့ ထောက်ခံချက်လည်း မရှိပါဘူး။) ဆက်သွယ်ရန်