Advanced UI Customization

Level: Advanced Module: Plugin Development 13 min read Lesson 31 of 47

Overview

  • What you’ll learn:
    • How iDempiere uses the ZK Ajax framework and how to extend existing windows and components programmatically
    • How to create custom dashboard gadgets, Info Windows, and custom editor components for specialized data entry
    • How to customize themes, toolbars, and keyboard panels, and deploy all UI modifications as OSGi plugins
  • Prerequisites: Lesson 15 — Building Your First Plugin, Lesson 4 — Navigating the User Interface
  • Estimated reading time: 24 minutes

Introduction

iDempiere’s web interface is built on the ZK Framework, a server-centric Ajax framework that provides rich desktop-like interactions in the browser. While the Application Dictionary handles most UI generation automatically — creating windows, tabs, and fields from metadata — there are scenarios where you need to go beyond what the dictionary can express. Custom dashboard gadgets, specialized editor components, theme modifications, and programmatic window extensions all require direct interaction with the ZK layer.

This lesson teaches you how to work with iDempiere’s UI framework at the code level. You will learn how ZK works, how to extend existing windows, how to build custom components, and how to package everything as deployable plugins.

ZK Ajax Framework Overview

Understanding ZK’s architecture is essential before customizing iDempiere’s UI.

Server-Centric Model

Unlike JavaScript-heavy frameworks (React, Angular), ZK follows a server-centric model. UI components exist as Java objects on the server. When a user interacts with the browser (clicking, typing, scrolling), ZK’s client engine sends Ajax requests to the server, which updates the component tree and sends back incremental UI updates. Developers write Java code, and ZK handles all JavaScript, HTML, and CSS generation.

// This Java code creates a button and handles its click — no JavaScript needed
Button button = new Button("Process Order");
button.addEventListener(Events.ON_CLICK, event -> {
    // This runs on the server when the user clicks
    processOrder();
    Clients.showNotification("Order processed successfully");
});

ZUL Templates

ZK also supports ZUL, an XML-based markup language for defining UI layouts declaratively:

<?xml version="1.0" encoding="UTF-8"?>
<window title="Custom Panel" border="normal" width="400px">
    <vlayout>
        <label value="Product Search" style="font-weight:bold"/>
        <hlayout>
            <textbox id="searchField" width="250px" placeholder="Enter product name"/>
            <button id="searchBtn" label="Search"/>
        </hlayout>
        <listbox id="resultList" height="300px" emptyMessage="No results">
            <listhead>
                <listheader label="Code" width="100px"/>
                <listheader label="Name" width="200px"/>
                <listheader label="Price" width="100px"/>
            </listhead>
        </listbox>
    </vlayout>
</window>

iDempiere uses a combination of programmatic Java code and ZUL templates for its UI. Custom forms and dashboard gadgets often use ZUL templates for layout, with Java classes providing the logic.

Key ZK Components Used by iDempiere

  • Window / Panel: Top-level containers for content areas
  • Tabbox / Tab / Tabpanel: Tabbed interface containers (used for AD tabs)
  • Grid / Rows / Row: Table-like layout for form fields
  • Listbox / Listitem: Scrollable list/grid for record display
  • Textbox, Intbox, Datebox, Combobox: Input components for different data types
  • Borderlayout / Vlayout / Hlayout: Layout containers for organizing components

Extending Existing Windows Programmatically

The most common UI customization is adding behavior to existing windows without modifying iDempiere core code. Model validators and event handlers allow you to intercept window events and modify behavior.

Using IFormController for Custom Window Logic

You can create a form controller that attaches custom logic to Application Dictionary windows:

import org.adempiere.webui.adwindow.ADWindow;
import org.adempiere.webui.event.ActionEvent;
import org.adempiere.webui.event.ActionListener;

public class CustomOrderWindowHandler implements ActionListener {

    private ADWindow adWindow;

    public void initialize(ADWindow window) {
        this.adWindow = window;
        // Listen for toolbar button events
        window.getADWindowContent().getToolbar()
            .addListener(this);
    }

    @Override
    public void actionPerformed(ActionEvent event) {
        String action = event.getActionCommand();
        if ("Process".equals(action)) {
            // Add custom validation before document processing
            if (!validateCustomRules()) {
                Dialog.warn(adWindow.getADWindowContent().getWindowNo(),
                    "Custom validation failed");
                event.setConsumed(true); // Prevent default processing
            }
        }
    }

    private boolean validateCustomRules() {
        // Custom validation logic
        return true;
    }
}

Adding Custom Buttons to Windows

You can add custom buttons to existing window toolbars through event handlers:

import org.adempiere.base.event.IEventTopics;
import org.adempiere.webui.adwindow.ADWindow;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;

@Component(
    property = {
        EventConstants.EVENT_TOPIC + "=" + IEventTopics.WINDOW_AFTER_OPEN
    }
)
public class WindowCustomizer implements EventHandler {

    @Override
    public void handleEvent(Event event) {
        ADWindow adWindow = (ADWindow) event.getProperty("ADWindow");
        int windowId = adWindow.getAD_Window_ID();

        // Only customize specific windows (e.g., Sales Order window)
        if (windowId == 143) {
            ToolBarButton customBtn = new ToolBarButton("CustomAction");
            customBtn.setLabel("Verify Inventory");
            customBtn.setTooltiptext("Check inventory availability for all lines");
            customBtn.addEventListener(Events.ON_CLICK, e -> {
                verifyInventory(adWindow);
            });
            adWindow.getADWindowContent().getToolbar().appendChild(customBtn);
        }
    }
}

Info Windows (AD_InfoWindow) Customization

Info Windows are the lookup/search dialogs that appear when users click the magnifying glass icon on a field. iDempiere allows you to customize these through the Application Dictionary or programmatically.

Application Dictionary Customization

Navigate to System Admin > General Rules > System Rules > Info Window to configure Info Windows:

  • Info Columns: Define which columns appear in the search results grid, their display order, and widths.
  • Search Criteria: Define which fields appear as search filters in the Info Window header.
  • SQL Validation: Add WHERE clause conditions to restrict which records appear (e.g., show only active customers).
  • Custom SQL: Override the default query with custom SQL for complex data requirements.

Programmatic Info Window Extension

For more complex customizations, extend the Info Window classes:

import org.adempiere.webui.info.InfoWindow;

public class CustomProductInfoWindow extends InfoWindow {

    @Override
    protected void initComponents() {
        super.initComponents();
        // Add a custom filter component
        Checkbox inStockOnly = new Checkbox();
        inStockOnly.setLabel("In Stock Only");
        inStockOnly.addEventListener(Events.ON_CHECK, e -> {
            refreshData();
        });
        addSearchComponent(inStockOnly);
    }

    @Override
    protected String buildWhereClause() {
        String where = super.buildWhereClause();
        // Add custom filter condition
        if (inStockOnly.isChecked()) {
            where += " AND QtyOnHand > 0";
        }
        return where;
    }
}

Custom ZK Components (Extending WEditor)

iDempiere uses WEditor subclasses to render different field types in windows. The editor system maps Application Dictionary display types to specific ZK components:

  • WStringEditor for text fields
  • WNumberEditor for numeric fields
  • WDateEditor for date fields
  • WTableDirEditor for table-direct lookups
  • WSearchEditor for search fields

Creating a Custom Editor

If you need a specialized input component — for example, a color picker, a barcode scanner input, or a map coordinate selector — create a custom WEditor:

import org.adempiere.webui.editor.WEditor;
import org.zkoss.zul.*;

public class WColorEditor extends WEditor {

    private Textbox colorInput;
    private Div colorPreview;

    public WColorEditor(GridField gridField) {
        super(new Hlayout(), gridField);
        initComponents();
    }

    private void initComponents() {
        Hlayout layout = (Hlayout) getComponent();

        colorInput = new Textbox();
        colorInput.setMaxlength(7);  // #RRGGBB
        colorInput.setWidth("80px");
        colorInput.setPlaceholder("#000000");
        colorInput.addEventListener(Events.ON_CHANGE, event -> {
            String color = colorInput.getValue();
            colorPreview.setStyle("background-color:" + color +
                ";width:24px;height:24px;border:1px solid #ccc;display:inline-block");
            // Notify the framework of the value change
            ValueChangeEvent vce = new ValueChangeEvent(
                this, getColumnName(), getOldValue(), color);
            fireValueChange(vce);
        });

        colorPreview = new Div();
        colorPreview.setStyle("width:24px;height:24px;border:1px solid #ccc;display:inline-block");

        layout.appendChild(colorInput);
        layout.appendChild(colorPreview);
    }

    @Override
    public String getDisplay() {
        return colorInput.getValue();
    }

    @Override
    public Object getValue() {
        return colorInput.getValue();
    }

    @Override
    public void setValue(Object value) {
        if (value == null) {
            colorInput.setValue("");
        } else {
            colorInput.setValue(value.toString());
            colorPreview.setStyle("background-color:" + value +
                ";width:24px;height:24px;border:1px solid #ccc;display:inline-block");
        }
    }
}

Registering a Custom Editor

Register your custom editor to handle a specific display type by implementing an editor factory:

import org.adempiere.webui.factory.IEditorFactory;

@Component(service = IEditorFactory.class)
public class CustomEditorFactory implements IEditorFactory {

    @Override
    public WEditor getEditor(GridTab gridTab, GridField gridField, boolean tableEditor) {
        // Use custom editor for fields with a specific display type or column name
        if ("Color".equals(gridField.getColumnName()) ||
            gridField.getDisplayType() == MY_CUSTOM_DISPLAY_TYPE) {
            return new WColorEditor(gridField);
        }
        return null; // Return null to let the default factory handle it
    }
}

Theme Customization

iDempiere’s visual appearance can be customized through CSS and ZUL template overrides without modifying core code.

CSS Customization

Create a theme plugin that overrides the default styles. The theme is loaded through a ZK theme provider:

/* custom-theme.css */

/* Override the main header color */
.z-north .desktop-header-left {
    background-color: #1a5276;
}

/* Customize the menu tree */
.z-west .z-tree {
    font-size: 13px;
}

/* Highlight required fields */
.mandatory-decorator-text .z-textbox,
.mandatory-decorator-text .z-intbox,
.mandatory-decorator-text .z-decimalbox {
    border-left: 3px solid #e74c3c;
}

/* Custom styling for specific windows */
.ad-window-content .z-grid {
    border: 1px solid #ddd;
    border-radius: 4px;
}

/* Dashboard gadget styling */
.dashboard-widget {
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    margin-bottom: 12px;
}

/* Responsive adjustments for smaller screens */
@media (max-width: 1024px) {
    .z-west {
        width: 200px !important;
    }
}

Registering a Theme Plugin

Create a theme provider to inject your custom CSS:

import org.adempiere.webui.theme.ITheme;
import org.osgi.service.component.annotations.Component;

@Component(service = ITheme.class, property = { "theme.id=custom-theme" })
public class CustomTheme implements ITheme {

    @Override
    public String[] getStyleSheets() {
        return new String[] { "~./css/custom-theme.css" };
    }

    @Override
    public String[] getScripts() {
        return null; // No custom JavaScript
    }
}

Place your CSS files in the plugin’s web/css/ directory and register the web resource path in your MANIFEST.MF.

Dashboard Gadgets

Dashboard gadgets are widgets displayed on the iDempiere home screen. Creating custom gadgets is one of the most impactful UI customizations because the dashboard is the first thing users see.

The DashboardPanel Interface

All dashboard gadgets implement the DashboardPanel interface or extend a base class:

import org.adempiere.webui.dashboard.DashboardPanel;
import org.zkoss.zul.*;

public class RecentOrdersGadget extends DashboardPanel {

    private Listbox orderList;

    public RecentOrdersGadget() {
        super();
        initComponents();
        loadData();
    }

    private void initComponents() {
        this.setTitle("Recent Sales Orders");

        Vlayout layout = new Vlayout();
        layout.setStyle("padding: 8px;");

        orderList = new Listbox();
        orderList.setMold("paging");
        orderList.setPageSize(10);
        orderList.setEmptyMessage("No recent orders");

        Listhead head = new Listhead();
        head.appendChild(new Listheader("Document No", null, "120px"));
        head.appendChild(new Listheader("Customer", null, "200px"));
        head.appendChild(new Listheader("Date", null, "100px"));
        head.appendChild(new Listheader("Total", null, "100px"));
        orderList.appendChild(head);

        layout.appendChild(orderList);
        this.appendChild(layout);
    }

    private void loadData() {
        Properties ctx = Env.getCtx();
        String sql = "SELECT o.DocumentNo, bp.Name, o.DateOrdered, o.GrandTotal " +
                     "FROM C_Order o " +
                     "JOIN C_BPartner bp ON o.C_BPartner_ID = bp.C_BPartner_ID " +
                     "WHERE o.IsSOTrx='Y' AND o.AD_Client_ID=? " +
                     "ORDER BY o.DateOrdered DESC";

        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            pstmt = DB.prepareStatement(sql, null);
            pstmt.setInt(1, Env.getAD_Client_ID(ctx));
            rs = pstmt.executeQuery();
            int count = 0;
            while (rs.next() && count++ < 10) {
                Listitem item = new Listitem();
                item.appendChild(new Listcell(rs.getString(1)));
                item.appendChild(new Listcell(rs.getString(2)));
                item.appendChild(new Listcell(rs.getTimestamp(3).toString()));
                item.appendChild(new Listcell(rs.getBigDecimal(4).toString()));
                orderList.appendChild(item);
            }
        } catch (Exception e) {
            log.log(Level.SEVERE, sql, e);
        } finally {
            DB.close(rs, pstmt);
        }
    }

    @Override
    public boolean isPooled() {
        return true; // Gadget can be pooled/reused across sessions
    }
}

Registering the Dashboard Gadget

Register your gadget in the Application Dictionary so users can add it to their dashboard:

  1. Navigate to System Admin > General Rules > System Rules > Dashboard Content.
  2. Create a new record with:
    • Name: Recent Sales Orders
    • ZUL File Path: Leave empty (for Java-based gadgets)
    • Class Name: com.example.dashboard.RecentOrdersGadget
    • Column No: 0 (left column), 1 (center), 2 (right)
    • Line No: Controls vertical position within the column
    • Is Show in Dashboard: Yes

Alternatively, register it via OSGi declarative services and a dashboard panel factory:

import org.adempiere.webui.factory.IDashboardGadgetFactory;

@Component(service = IDashboardGadgetFactory.class)
public class CustomGadgetFactory implements IDashboardGadgetFactory {

    @Override
    public DashboardPanel getGadget(String uri, Component parent) {
        if ("recent-orders".equals(uri)) {
            return new RecentOrdersGadget();
        }
        return null;
    }
}

Toolbar Customization

The window toolbar in iDempiere contains standard buttons for navigation, CRUD operations, and document processing. You can customize it by adding, removing, or modifying toolbar buttons.

Adding a Custom Toolbar Button

// Using the window event handler approach
@Override
public void handleEvent(Event event) {
    ADWindow adWindow = (ADWindow) event.getProperty("ADWindow");

    ToolBarButton exportBtn = new ToolBarButton();
    exportBtn.setImage(ThemeManager.getThemeResource("images/Export24.png"));
    exportBtn.setTooltiptext("Export to Excel");
    exportBtn.addEventListener(Events.ON_CLICK, e -> {
        exportCurrentView(adWindow);
    });

    // Add button after the standard toolbar buttons
    adWindow.getADWindowContent().getToolbar().appendChild(new Space());
    adWindow.getADWindowContent().getToolbar().appendChild(exportBtn);
}

Hiding Standard Buttons

To hide standard toolbar buttons for specific windows (e.g., hide the Delete button on read-only windows):

ToolBarButton deleteBtn = adWindow.getADWindowContent().getToolbar()
    .getButton("Delete");
if (deleteBtn != null) {
    deleteBtn.setVisible(false);
}

Window Event Handling

ZK’s event system allows you to intercept and respond to any user interaction. Common events you can handle in iDempiere windows:

// Listen for tab change events
adWindow.getADWindowContent().getADTab()
    .addEventListener(ADTabpanel.ON_ACTIVATE_EVENT, event -> {
        int tabIndex = adWindow.getADWindowContent().getADTab().getSelectedIndex();
        // Perform custom logic when a specific tab is activated
    });

// Listen for field value changes
gridField.addEventListener(Events.ON_CHANGE, event -> {
    Object newValue = gridField.getValue();
    // React to the field value change
});

// Listen for grid row selection
listbox.addEventListener(Events.ON_SELECT, event -> {
    Listitem selected = listbox.getSelectedItem();
    // Handle row selection
});

Form vs. Window Customization

Understanding when to use a custom form versus customizing an AD window is important for choosing the right approach:

Aspect AD Window Customization Custom Form
Data entry for AD tables Preferred — uses AD metadata Overkill for simple data entry
Complex UI layout Limited by AD structure Full control over layout
Multi-step wizard Not supported Ideal approach
Dashboard-like display Not applicable Good fit
Interactive processing Limited Full control
Maintenance effort Low (AD-driven) Higher (custom code)

Use AD window customization (model validators, callouts, event handlers) when you need to modify behavior of standard data entry windows. Use custom forms when you need a completely different UI paradigm, such as a multi-step wizard, a graphical process monitor, or an interactive dashboard.

Deploying UI Customizations as Plugins

All UI customizations should be packaged as OSGi plugins for clean deployment and maintainability:

  1. Create a plugin project with dependencies on org.adempiere.ui.zk and org.adempiere.base.
  2. Place ZUL templates in the plugin’s web/ directory, which is accessible via the ~./ URL prefix in ZK.
  3. Place CSS and images in web/css/ and web/images/ respectively.
  4. Register components using OSGi declarative services (@Component annotations).
  5. Test the plugin by deploying it to your development iDempiere instance and verifying that UI changes appear correctly.
  6. Export as a JAR for deployment to production via the P2 update mechanism.

Bundle your plugin’s MANIFEST.MF to export web resources:

Web-ContextPath: /webui
Bundle-ClassPath: .,
 web/

Summary

You now have a comprehensive understanding of iDempiere’s UI customization capabilities. From extending existing windows with event handlers and custom toolbar buttons, to creating entirely new dashboard gadgets and editor components, to restyling the entire application with custom themes — all deployed cleanly as OSGi plugins. The ZK framework provides the component model, iDempiere provides the extension points, and OSGi provides the deployment infrastructure. In the next lesson, you will learn how to create professional reports using JasperReports integrated with iDempiere.

繁體中文翻譯

概覽

  • 您將學到:
    • iDempiere 如何使用 ZK Ajax 框架,以及如何以程式化方式擴展現有視窗和元件
    • 如何建立自訂儀表板小工具、資訊視窗和自訂編輯器元件以進行專門的資料輸入
    • 如何自訂主題、工具列和鍵盤面板,以及將所有 UI 修改部署為 OSGi 外掛
  • 先備知識: 第 15 課 — 建構您的第一個外掛、第 4 課 — 導覽使用者介面
  • 預估閱讀時間: 24 分鐘

簡介

iDempiere 的 Web 介面建構在 ZK 框架之上,這是一個以伺服器為中心的 Ajax 框架,在瀏覽器中提供豐富的桌面級互動。雖然應用程式字典自動處理大多數 UI 生成 — 從中繼資料建立視窗、頁籤和欄位 — 但在某些場景中,您需要超越字典所能表達的範圍。自訂儀表板小工具、專用編輯器元件、主題修改和程式化視窗擴展都需要直接與 ZK 層互動。

本課教您如何在程式碼層級使用 iDempiere 的 UI 框架。您將學習 ZK 的工作原理、如何擴展現有視窗、如何建構自訂元件,以及如何將所有內容打包為可部署的外掛。

ZK Ajax 框架概覽

在自訂 iDempiere 的 UI 之前,理解 ZK 的架構至關重要。

以伺服器為中心的模型

與以 JavaScript 為主的框架(React、Angular)不同,ZK 遵循以伺服器為中心的模型。UI 元件以 Java 物件形式存在於伺服器上。當使用者在瀏覽器中互動(點擊、打字、捲動)時,ZK 的用戶端引擎發送 Ajax 請求到伺服器,伺服器更新元件樹並發送回增量 UI 更新。開發人員撰寫 Java 程式碼,ZK 處理所有 JavaScript、HTML 和 CSS 生成。

iDempiere 使用的關鍵 ZK 元件

  • Window / Panel: 內容區域的頂層容器
  • Tabbox / Tab / Tabpanel: 分頁式介面容器(用於 AD 頁籤)
  • Grid / Rows / Row: 表單欄位的表格式佈局
  • Listbox / Listitem: 記錄顯示的可捲動清單/網格
  • Textbox、Intbox、Datebox、Combobox: 不同資料類型的輸入元件
  • Borderlayout / Vlayout / Hlayout: 用於組織元件的佈局容器

以程式化方式擴展現有視窗

最常見的 UI 自訂是在不修改 iDempiere 核心程式碼的情況下為現有視窗加入行為。模型驗證器和事件處理器允許您攔截視窗事件並修改行為。

資訊視窗(AD_InfoWindow)自訂

資訊視窗是使用者點擊欄位上的放大鏡圖示時出現的查詢/搜尋對話框。iDempiere 允許您透過應用程式字典或程式化方式自訂這些視窗。

自訂 ZK 元件(擴展 WEditor)

iDempiere 使用 WEditor 子類別在視窗中呈現不同的欄位類型。編輯器系統將應用程式字典顯示類型對應到特定的 ZK 元件。

主題自訂

iDempiere 的視覺外觀可以透過 CSS 和 ZUL 範本覆蓋進行自訂,無需修改核心程式碼。

儀表板小工具

儀表板小工具是顯示在 iDempiere 首頁上的元件。建立自訂小工具是最有影響力的 UI 自訂之一,因為儀表板是使用者看到的第一個畫面。

工具列自訂

iDempiere 中的視窗工具列包含用於導覽、CRUD 操作和文件處理的標準按鈕。您可以透過加入、移除或修改工具列按鈕來自訂它。

表單與視窗自訂的比較

了解何時使用自訂表單與自訂 AD 視窗對於選擇正確的方法很重要。當您需要修改標準資料輸入視窗的行為時,使用 AD 視窗自訂(模型驗證器、呼叫返回、事件處理器)。當您需要完全不同的 UI 模式時,使用自訂表單,例如多步驟精靈、圖形化流程監控器或互動式儀表板。

將 UI 自訂部署為外掛

所有 UI 自訂應打包為 OSGi 外掛以實現乾淨的部署和可維護性。

總結

您現在全面了解了 iDempiere 的 UI 自訂功能。從使用事件處理器和自訂工具列按鈕擴展現有視窗,到建立全新的儀表板小工具和編輯器元件,再到使用自訂主題重新設計整個應用程式的樣式 — 所有內容都作為 OSGi 外掛進行乾淨的部署。ZK 框架提供元件模型,iDempiere 提供擴展點,OSGi 提供部署基礎設施。在下一課中,您將學習如何使用整合了 iDempiere 的 JasperReports 建立專業報表。

日本語翻訳

概要

  • 学習内容:
    • iDempiere が ZK Ajax フレームワークをどのように使用し、既存のウィンドウとコンポーネントをプログラムで拡張する方法
    • カスタムダッシュボードガジェット、情報ウィンドウ、専門的なデータ入力用のカスタムエディターコンポーネントを作成する方法
    • テーマ、ツールバー、キーボードパネルをカスタマイズし、すべての UI 変更を OSGi プラグインとしてデプロイする方法
  • 前提条件: レッスン 15 — 最初のプラグインの構築、レッスン 4 — ユーザーインターフェースのナビゲーション
  • 推定読了時間: 24 分

はじめに

iDempiere の Web インターフェースは ZK フレームワーク上に構築されています。これはサーバー中心の Ajax フレームワークで、ブラウザでリッチなデスクトップライクなインタラクションを提供します。アプリケーションディクショナリがメタデータからウィンドウ、タブ、フィールドを自動的に生成してほとんどの UI 生成を処理しますが、ディクショナリで表現できる範囲を超える必要があるシナリオがあります。カスタムダッシュボードガジェット、専用のエディターコンポーネント、テーマの変更、プログラムによるウィンドウ拡張はすべて、ZK レイヤーとの直接的な対話が必要です。

このレッスンでは、コードレベルで iDempiere の UI フレームワークを操作する方法を学びます。ZK の仕組み、既存のウィンドウの拡張方法、カスタムコンポーネントの構築方法、すべてをデプロイ可能なプラグインとしてパッケージ化する方法を学びます。

ZK Ajax フレームワークの概要

iDempiere の UI をカスタマイズする前に、ZK のアーキテクチャを理解することが不可欠です。

サーバー中心モデル

JavaScript 中心のフレームワーク(React、Angular)とは異なり、ZK はサーバー中心のモデルに従います。UI コンポーネントはサーバー上の Java オブジェクトとして存在します。ユーザーがブラウザで操作すると(クリック、入力、スクロール)、ZK のクライアントエンジンが Ajax リクエストをサーバーに送信し、サーバーがコンポーネントツリーを更新して増分的な UI 更新を返送します。開発者は Java コードを書き、ZK がすべての JavaScript、HTML、CSS の生成を処理します。

iDempiere で使用される主要な ZK コンポーネント

  • Window / Panel: コンテンツエリアのトップレベルコンテナ
  • Tabbox / Tab / Tabpanel: タブ付きインターフェースコンテナ(AD タブに使用)
  • Grid / Rows / Row: フォームフィールドのテーブルライクなレイアウト
  • Listbox / Listitem: レコード表示用のスクロール可能なリスト/グリッド
  • Textbox、Intbox、Datebox、Combobox: 異なるデータ型の入力コンポーネント
  • Borderlayout / Vlayout / Hlayout: コンポーネントを整理するためのレイアウトコンテナ

既存ウィンドウのプログラムによる拡張

最も一般的な UI カスタマイズは、iDempiere のコアコードを変更せずに既存のウィンドウに動作を追加することです。モデルバリデーターとイベントハンドラーを使用して、ウィンドウイベントをインターセプトし、動作を変更できます。

情報ウィンドウ(AD_InfoWindow)のカスタマイズ

情報ウィンドウは、ユーザーがフィールドの虫眼鏡アイコンをクリックしたときに表示されるルックアップ/検索ダイアログです。iDempiere では、アプリケーションディクショナリまたはプログラムでこれらをカスタマイズできます。

カスタム ZK コンポーネント(WEditor の拡張)

iDempiere は WEditor サブクラスを使用して、ウィンドウ内のさまざまなフィールドタイプをレンダリングします。エディターシステムは、アプリケーションディクショナリの表示タイプを特定の ZK コンポーネントにマッピングします。

テーマのカスタマイズ

iDempiere のビジュアル外観は、コアコードを変更することなく、CSS および ZUL テンプレートのオーバーライドでカスタマイズできます。

ダッシュボードガジェット

ダッシュボードガジェットは iDempiere のホーム画面に表示されるウィジェットです。カスタムガジェットの作成は、ダッシュボードがユーザーが最初に目にする画面であるため、最もインパクトのある UI カスタマイズの 1 つです。

ツールバーのカスタマイズ

iDempiere のウィンドウツールバーには、ナビゲーション、CRUD 操作、伝票処理用の標準ボタンが含まれています。ツールバーボタンの追加、削除、変更によってカスタマイズできます。

フォームとウィンドウのカスタマイズの比較

カスタムフォームと AD ウィンドウのカスタマイズのどちらを使用するかを理解することは、正しいアプローチを選択するために重要です。標準的なデータ入力ウィンドウの動作を変更する必要がある場合は AD ウィンドウのカスタマイズ(モデルバリデーター、コールアウト、イベントハンドラー)を使用します。マルチステップウィザード、グラフィカルなプロセスモニター、インタラクティブなダッシュボードなど、まったく異なる UI パラダイムが必要な場合はカスタムフォームを使用します。

UI カスタマイズをプラグインとしてデプロイ

すべての UI カスタマイズは、クリーンなデプロイメントと保守性のために OSGi プラグインとしてパッケージ化する必要があります。

まとめ

iDempiere の UI カスタマイズ機能について包括的に理解しました。イベントハンドラーやカスタムツールバーボタンによる既存ウィンドウの拡張から、まったく新しいダッシュボードガジェットやエディターコンポーネントの作成、カスタムテーマによるアプリケーション全体のスタイル変更まで、すべて OSGi プラグインとしてクリーンにデプロイされます。ZK フレームワークがコンポーネントモデルを提供し、iDempiere が拡張ポイントを提供し、OSGi がデプロイメントインフラストラクチャを提供します。次のレッスンでは、iDempiere と統合された JasperReports を使用してプロフェッショナルなレポートを作成する方法を学びます。

You Missed