DataGrid 数据网格

DataGrid - 数据网格 组件,与 Table 组件类似,适合用于展示、排序表格类数据,由于使用了在滚动时加载数据的延迟加载方式,所以此组件具有更好的数据行、列操作性能。

除此之外,DataGrid 还有以下功能:

但是 DataGrid 没有数据分组的功能,而 Table 组件支持 分组

组件的 XML 名称:dataGrid

基本用法

一个典型的 dataGrid 如下所示:

data grid anatomy
  1. 按钮面板

  2. 排序按钮

  3. 分页器

  4. 列控制按钮

  5. 数据行

  6. 表头行

以下为在 XML 文件中定义 dataGrid 的示例:

<data>
    <collection id="customersDc" class="ui.ex1.entity.Customer">
        <fetchPlan extends="_base"/>
        <loader id="customersDl">
            <query>
                <![CDATA[select e from uiex1_Customer e]]>
            </query>
        </loader>
    </collection>
</data>
<layout>
    <dataGrid id="customersGrid" width="100%" dataContainer="customersDc">
        <actions>
            <action id="create" type="create"/>
            <action id="edit" type="edit"/>
            <action id="remove" type="remove"/>
        </actions>
        <buttonsPanel alwaysVisible="true">
            <button id="customersGridCreateBtn" action="customersGrid.create"/>
            <button id="customersGridEditBtn" action="customersGrid.edit"/>
            <button id="customersGridRemoveBtn" action="customersGrid.remove"/>
        </buttonsPanel>
        <simplePagination/>
        <columns>
            <column id="hobby" property="hobby"/>
            <column id="firstName" property="firstName"/>
            <column id="lastName" property="lastName"/>
            <column id="age" property="age"/>
            <column id="email" property="email"/>
            <column id="level" property="level"/>
            <column id="rewardPoints" property="rewardPoints"/>
        </columns>
    </dataGrid>
</layout>

其中,为 Customer 实体使用了 集合数据容器DataGrid 组件通过 dataContainer 属性与数据容器绑定,columns 元素定义那些实体属性要显示在数据网格的列中。

数据绑定

声明式绑定

通常,使用 dataContainer 属性在界面 XML 描述中将数据与 dataGrid 绑定。此时需要使用一个 集合数据容器

使用键值对容器

数据网格还可以绑定至 键值对容器 展示查询的纯数值和聚合值。示例:

<data>
    <keyValueCollection id="salesDc">
        <loader id="salesLoader">
            <query>
                <![CDATA[select o.customer, o.customer.firstName,
                sum(o.amount) from uiex1_Order o group by o.customer]]>
            </query>
        </loader>
        <properties>
            <property class="ui.ex1.entity.Customer" name="customerEntity"/>
            <property datatype="string" name="customerName"/>
            <property datatype="decimal" name="sum"/>
        </properties>
    </keyValueCollection>
</data>
<layout>
    <dataGrid id="keyValueGrid" width="100%" dataContainer="salesDc">
        <columns>
            <column id="customerName" caption="Customer"/>
            <column id="sum" caption="Summary amount"/>
        </columns>
    </dataGrid>
</layout>

编程式绑定

如需以编程的方式在界面控制器定义数据容器,则可以在 XML 中设置 metaClass 属性而非 dataContainer

<dataGrid id="customersDataGrid"
          width="100%"
          metaClass="uiex1_Customer">
    <columns>
        <column id="firstName" property="firstName"/>
        <column id="lastName" property="lastName"/>
    </columns>
</dataGrid>

界面控制器中,使用 ContainerDataGridItems 类将数据网格和数据容器绑定:

@Autowired
private DataGrid<Customer> customersDataGrid;

@Autowired
private CollectionContainer<Customer> customersDc;


@Subscribe
public void onInit(InitEvent event) {
    customersDataGrid.setItems(new ContainerDataGridItems<>(customersDc));
}

columns 元素

数据网格的列集合定义在 columns 元素中。如果未指定,则通过 dataContainer 定义的 fetch plan 自动确定。

columns 元素有如下属性:

  • includeAll 加载 dataContainer 中定义的 fetchPlan 的所有属性。

    在下面的例子中,我们显示了 customersDc 中使用 fetch plan 的所有属性:

    <dataGrid id="gridIncludeAll"
              width="100%"
              dataContainer="customersDc">
        <columns includeAll="true"/>
    </dataGrid>

    如果实体的 fetch plan 包含引用属性,该属性会按照其 实例名 进行展示。如果需要展示一个特定的级联属性,则需要在 fetch plan 和 column 元素中定义:

    <data>
        <collection id="customersDc1" class="ui.ex1.entity.Customer">
            <fetchPlan extends="_base">
                <property name="city" fetchPlan="_base">
                    <property name="country" fetchPlan="_base"/>
                </property>
            </fetchPlan>
            <loader id="customersDl1">
                <query>
                    <![CDATA[select e from uiex1_Customer e]]>
                </query>
            </loader>
        </collection>
    </data>
    <layout>
        <dataGrid id="gridIncludeAllReference"
                  width="100%"
                  dataContainer="customersDc1">
            <columns includeAll="true">
                <column id="city.country.name"/>
            </columns>
        </dataGrid>
    </layout>
  • exclude 英文逗号分隔的属性列表,这些属性不会被加载到数据网格。

    在下面的例子中,我们会显示除了 idmaritalStatusemail 之外的所有属性:

    <dataGrid id="gridExclude"
              width="100%"
              dataContainer="customersDc">
        <columns includeAll="true"
                 exclude="id,maritalStatus,email"/>
    </dataGrid>

column 元素

每一列是在内部的 column 元素中描述。

id - 非必需属性,指定列标识。如果没有设置,对应的 property 值会被用作该列的标识。如果 idproperty 都不设置,会抛出 GuiDevelopmentException 异常。对于 代码生成列,则 id 属性是必需的。

property - 指定列对应的实体属性。可以是数据容器实体的属性,也可以是关联实体的属性,关联实体属性前面需要加上关联类名字并通过“.”连接。例如:

<columns>
    <column property="firstName"/>
    <column property="lastName"/>
    <column property="city.name"/>
    <column property="city.country.name"/>
</columns>

如需在 Jmix Studio 中添加 column,可以在界面 XML 或者 Component Hierarchy 面板中选择 columns 元素,然后点击 Component Inspector 面板的Add→Column 按钮。

列标题

caption - 可选属性,定义列标题。如果未设置,会使用实体属性的 本地化名称

隐藏列开关中也使用这个标题展示列名,除非在 collapsingToggleCaption 属性单独设置。

列折叠

collapsed - 可选属性,设置为 true 时自动隐藏该列。collapsed 属性默认值为 false。用户可以通过数据网格右上角的 table column control button 按钮提供的菜单控制列的可见性,此时需要设置 columnsCollapsingAllowedtrue

collapsible 定义用户是否可以通过 DataGrid 表格组件的列控制按钮隐藏/显示该列。默认为 true

collapsingToggleCaption 设置在列控制按钮中该列的标题。默认为 null,此时列控制按钮中该列的标题与数据网格中该列的 caption 一致。

<dataGrid id="collapsedGrid"
          width="100%"
          dataContainer="customersDc">
    <columns>
        <column property="firstName"
                collapsible="false"/>
        <column property="lastName"
                collapsible="false"/>
        <column property="age"
                collapsed="true"/>
        <column property="hobby"
                collapsingToggleCaption="The favourite customer's hobby"/>
        <column property="level"/>
        <column property="city"/>
    </columns>
</dataGrid>
data grid collapse

可以看到,age 列默认是折叠不显示的,但是用户可以通过 table column control button 按钮的下拉菜单显示它。

另外,firstNamelastName 列是不能隐藏的。

列控制按钮的下拉项中,展示了 hobby 列的自定义标题。

列折叠可以通过事件 ColumnCollapsingChangeEvent 进行跟踪。

列宽度

width - 可选属性,定义列宽。只支持以像素为单位的数值类型。

<columns>
    <column property="firstName" width="120"/>
    <column property="lastName" width="150"/>
    <column property="city" width="100"/>
</columns>
data grid column width

expandRatio - 可选属性,设置列宽占比。比值必须大于等于 0。如果至少有一列设置了其他值,则忽略所有的隐式值,并且只使用设置的值。

默认情况下,所有列等宽(expandRatio = 1)。

如果同时设置了 widthexpandRatio 属性,则 expandRatio 会被忽略。

<columns>
    <column property="firstName" expandRatio="0"/>
    <column property="lastName" expandRatio="1"/>
    <column property="city" expandRatio="2"/>
</columns>
data grid ratio

上面的例子中,DataGrid 组件有三列,列宽占比分别为 012。占比为 0 的列宽与内容所需的宽度一致。占比为 1 的列宽除了展示内容所需的宽度之外,还占用了剩余宽度的 1/3。占比为 2 的列宽除了展示内容所需的宽度之外,还占用了剩余宽度的 2/3。

如需清除占比值,可以为 expandRatio 属性设置一个负值。

minimumWidth 定义占比时列可占用的最小列宽,以像素为单位。

maximumWidth 定义占比时列可占用的最大列宽,以像素为单位。

调整列宽

用户可以调整列宽:

data grid resize

resizable 属性定义用户是否可以调整此列。默认所有列都可手动调整。

columnResizeMode 设置调整列宽时的动画效果。支持两种效果:

  • ANIMATED,动画效果,列宽跟随鼠标拖拽(默认)。

  • SIMPLE,简单效果,列宽会在拖拽动作结束后才发生改变。

列宽变化事件可以通过 ColumnResizeEvent 跟踪。

列重排

DataGrid 提供拖拽功能,用户可以改变数据网格内列的展示顺序。

table columns reordering

列重排的功能是默认开启的。如需禁用,可设置 reorderingAllowed 属性为 false

列顺序改变可用 ColumnReorderEvent 跟踪。

列锁定

DataGrid 支持将列固定在左侧。在查看有很多列的数据时,重要的列可以固定在左边总是可见。固定的列不会随着水平滚动条移动。

设置固定列的个数。0 表示不需要固定任何列,除了使用 复选框选择模式 开启多选时的预定义复选框列。设为 -1 的时候即使选择列也不固定。默认值为 0

下面的示例是左侧固定两列的数据网格。

<dataGrid id="frozenGrid"
          width="100%" footerVisible="false"
          dataContainer="customersDc"
          frozenColumnCount="2">
    <columns>
        <column property="firstName"/>
        <column property="lastName"/>
        <column property="age"/>
        <column property="email"/>
        <column property="level"/>
        <column property="rewardPoints"/>
        <column property="hobby"/>
        <column property="city"/>
    </columns>
</dataGrid>
data grid frozen

尺寸

数据网格尺寸

除了 heightwidth 属性之外,DataGrid 还有 minHeightminWidth 两个可选属性:

  • minHeight 设置 DataGrid 组件的最小高度。

  • minWidth 设置 DataGrid 组件的最小宽度。

列尺寸

参阅 列宽度 章节的 widthexpandRatiominimumWidthmaximumWidth 属性。

行尺寸

headerRowHeight 属性设置 header 高度。

bodyRowHeight 属性设置数据行高度(单位像素)。如果值为 -1,会在 DataGrid 显示前基于主题中空行的高度计算数据行的高度。

footerRowHeight 属性设置 footer 高度。

下面示例中,DataGrid 组件有自定义的表头和数据行高度:

<dataGrid id="sizedGrid"
          width="100%"
          dataContainer="customersDc"
          headerRowHeight="36"
          bodyRowHeight="28">
    <columns>
        <column id="firstName" property="firstName"/>
        <column id="lastName" property="lastName"/>
        <column id="level" property="level"/>
        <column id="age" property="age"/>
        <column id="rewardPoints" property="rewardPoints"/>
    </columns>
</dataGrid>
data grid custom row height

选择

DataGrid 组件支持对数据行进行单选和多选。

selectionMode 属性设置行选择模式。有四种预定义的模式:

  • SINGLE - 单选;

  • MULTI - 多选;

  • MULTI_CHECK - 复选框多选;

  • NONE - 不可选择。

行选择事件可以用 SelectionEvent 跟踪。

单选模式

selectionMode 属性设置为 SINGLE 时,用户一次只能选择单一的数据行。这是数据网格默认的选择模式。

多选模式

selectionMode 属性设置为 MULTI 时,用户可以在数据网格中按下 CtrlShift 建后,用键盘或鼠标选取多行。

复选框选择模式

selectionMode 属性设置为 MULTI_CHECK 时,用户可以用最左列的复选框选择多行。

data grid checkbox select

禁用选择

selectionMode 属性设置为 NONE 时,用户不能选择。

选择文本

textSelectionEnabled 属性定义是否可以在单元格中选取文本。默认值 false

空状态

在数据容器没有数据或者没有设置数据容器时,支持为 DataGrid 设置展示空状态消息或链接。

空状态消息通过 emptyStateMessage 属性定义。可以用于显示无数据的原因。

示例:

<dataGrid id="placeholderGrid"
          width="100%"
          height="200"
          metaClass="uiex1_Customer"
          emptyStateMessage="No customers added."
          emptyStateLinkMessage="Add customer">
    <columns>
        <column id="firstName" property="firstName"/>
        <column id="lastName" property="lastName"/>
        <column id="level" property="level"/>
        <column id="age" property="age"/>
        <column id="rewardPoints" property="rewardPoints"/>
    </columns>
</dataGrid>
data grid placeholder

emptyStateMessageemptyStateLinkMessage 属性都可以从 消息包 中加载本地化消息。

EmptyStateLinkClickHandler 处理空状态链接的点击。

列可见控制

用户可以用数据网格 header 右侧的 table column control button 列控制按钮选择 折叠 隐藏哪些列。

当前展示列在下拉列表中是勾选状态。其他的菜单项:

  • Select all - 展示所有的数据网格列。

  • Deselect all - 隐藏所有能隐藏的列。

data grid column collapsing allowed

如果 columnsCollapsingAllowed 属性设置为 false,则用户不能折叠列。默认值是 true

Headers 和 Footers

headerVisible 属性定义是否显示 DataGrid header。默认值是 true

使用 headerRowHeight 属性设置 header 的行高(单位是像素)。如果值设置为 -1,则在 DataGrid 展示之前根据主题中空行的高度计算行高。默认值是 -1

HeaderRowFooterRow 接口分别用来展示 header 和 footer 单元格。这些单元格可以是合并列。

DataGrid 的下列方法支持创建和管理 DataGrid 的 header 和 footer:

  • appendHeaderRow()appendFooterRow() - 在 header/footer 底部添加新行。

  • prependHeaderRow()prependFooterRow() - 在 header/footer 顶部添加新行。

  • addHeaderRowAt()addFooterRowAt() - 在 header/footer 中的指定位置添加新行。当前行和后面已经存在的行依次下移且索引值相应增加。

  • removeHeaderRow()removeFooterRow() - 从 header/footer 删除指定的行。

  • getHeaderRowCount()getFooterRowCount() - 获取 header/footer 的行数。

  • setDefaultHeaderRow() - 设置 header 的默认行。默认行是一个特殊的 header,提供列排序的功能。

HeaderCellFooterCell 接口提供自定义静态单元格功能:

  • setStyleName() - 为单元格设置自定义样式名称。

  • getCellType() - 返回单元格内容类型。静态单元格枚举类型 DataGridStaticCellType 有三个值:

    • TEXT

    • HTML

    • COMPONENT

  • getComponent()getHtml()getText() - 不同类型单元格获取内容的方法。

下面这个例子中,header 包含合并的单元格,footer 显示经计算得出的值。

<dataGrid id="dataGrid"
          width="100%"
          footerRowHeight="140"
          headerRowHeight="40"
          dataContainer="customersDc">
    <columns>
        <column property="firstName"/>
        <column property="lastName"/>
        <column property="age"/>
        <column property="level"/>
        <column property="rewardPoints"/>
    </columns>
</dataGrid>
@Autowired
private DataGrid<Customer> dataGrid;
@Autowired
private CollectionLoader<Customer> customersDl;

private int silverCount = 0;
private int goldCount = 0;
private int platinumCount = 0;
private int diamondCount = 0;


@Subscribe
public void onInit(InitEvent event) {
    customersDl.load();
    initFooter();
    initHeader();
}

private void initFooter() {
    DataGrid.FooterRow footerRow = dataGrid.prependFooterRow();
    DataGrid.FooterCell footerCell = footerRow.join("firstName","lastName");
    footerCell.setHtml("<strong>Total customers count: " +
            customersDc.getItems().size() + "</strong>");
    calculateMemberCount();
    footerRow.getCell("age").setHtml("<strong>Average age: " + getAverage("age") +
            "</strong>");
    footerRow.getCell("level").setHtml("<strong>Silver Members: " + silverCount +
            "<br>" + "Gold Members: " + goldCount + "<br>" +
            "Platinum Members: " + platinumCount + "<br>" +
            "Diamond Members: " + diamondCount + "<br>" +
            "</strong>");
    footerRow.getCell("rewardPoints").setHtml("<strong>Average reward points: " +
            getAverage("rewardPoints") + "</strong>");
}

private void initHeader() {
    DataGrid.HeaderRow headerRow = dataGrid.prependHeaderRow();
    DataGrid.HeaderCell headerCell = headerRow.join("firstName", "lastName");
    headerCell.setText("Full name");
    headerCell.setStyleName("center-bold");
    headerCell = headerRow.join("level", "rewardPoints");
    headerCell.setText("Account information");
    headerCell.setStyleName("center-bold");
}

private int getAverage(String propertyId) {
    double average = 0.0;
    Collection<Customer> items = customersDc.getItems();
    for (Customer customer : items) {
        Double value = propertyId.equals("rewardPoints") ?
                customer.getRewardPoints().doubleValue() :
                customer.getAge().doubleValue();
        average += value != null ? value : 0.0;
    }
    return (int) (average / items.size());
}

private void calculateMemberCount() {
    Collection<Customer> items = customersDc.getItems();
    for (Customer customer : items) {
        switch (customer.getLevel()) {
            case SILVER:
                silverCount++;
                break;
            case GOLD:
                goldCount++;
                break;
            case PLATINUM:
                platinumCount++;
                break;
            case DIAMOND:
                diamondCount++;
                break;
        }
    }
}
data grid header footer

聚合

DataGrid 支持对行内值进行聚合运算。

支持下列运算符:

  • SUM - 求和

  • AVG - 求平均值

  • COUNT - 计数

  • MIN - 最小值

  • MAX - 最大值

如需启用对 DataGrid 的行进行聚合运算,按照下列配置:

  1. 设置 aggregatable 属性值为 true

  2. 为聚合列设置 aggregation 元素。

  3. 设置 aggregation 元素的 type 属性,表示聚合的函数。

如需在 Jmix Studio 中定义 aggregation,可以在界面 XML 或者 Component Hierarchy 面板中选择聚合单元格的列,然后点击 Component Inspector 面板的 Add→Aggregation 按钮。

聚合值展示在附加行中。

aggregationPosition 属性可以设置聚合值行的位置:TOPBOTTOM。默认为 TOP

默认情况下,聚合列只支持数字类型,比如 IntegerDoubleLongBigDecimal

带聚合列的 DataGrid XML 描述示例:

<dataGrid id="aggregationGrid"
          width="100%"
          dataContainer="customersDc1"
          aggregatable="true">
    <columns>
        <column id="firstName" property="firstName"/>
        <column id="lastName" property="lastName"/>
        <column id="level" property="level"/>
        <column id="age" property="age">
            <aggregation type="AVG"/>
        </column>
        <column id="rewardPoints" property="rewardPoints"/>
    </columns>
</dataGrid>
data grid aggregation

valueDescription 属性定义一个提示,当用户的光标悬停在聚合值上时通过弹出框展示这个提示。对于上面列出的运算(SUMAVGCOUNTMINMAX),提示弹窗已经默认开启。

可以指定不同于 Datatype 标准格式的 formatter 显示聚合值:

<column id="amount" property="amount">
    <aggregation type="SUM">
        <formatter>
            <number format="#,##0.00"/>
        </formatter>
    </aggregation>
</column>

aggregation 元素还可以包含 strategyClass 属性,指定一个实现 AggregationStrategy 接口的类。

public class CustomerHobbyAggregation implements AggregationStrategy<Hobby, String> {
    @Override
    public String aggregate(Collection<Hobby> propertyValues) {
        Hobby mostFrequent = null;
        long max = 0;
        if (CollectionUtils.isNotEmpty(propertyValues)) {
            for (Hobby hobby : Hobby.values()) {
                long current = propertyValues.stream()
                        .filter(customerHobby -> customerHobby.equals(hobby))
                        .count();

                if (current > max) {
                    mostFrequent = hobby;
                    max = current;
                }
            }
        }

        if (mostFrequent != null) {
            return String.format("%s: %d/%d",
                    mostFrequent.name(), max, propertyValues.size());
        }

        return null;
    }

    @Override
    public Class<String> getResultClass() {
        return String.class;
    }
}
<dataGrid id="gridAggregationStrategy"
          width="100%"
          aggregatable="true"
          dataContainer="customersDc">
    <columns>
        <column id="firstName" property="firstName"/>
        <column id="lastName" property="lastName"/>
        <column id="hobby" property="hobby">
            <aggregation
                strategyClass="ui.ex1.screen.component.datagrid.CustomerHobbyAggregation"/>
        </column>
    </columns>
</dataGrid>

排序

DataGrid 支持对列数据进行排序。排序功能默认开启。

sortable 属性可以启用数据网格的排序功能。默认为 true。开启后,点击列名会在列名右边显示 table sortable down/table sortable up 图标。

按住 Shift 的同时选择多列可以按多列排序。

使用特定 sortable 属性可以禁用该列的排序功能。

下面的例子中,我们禁用了 lastName 列的排序:

<columns>
    <column id="firstName" property="firstName"/>
    <column id="lastName" property="lastName" sortable="false"/>
    <column id="city" property="city"/>
    <column id="hobby" property="hobby"/>
</columns>

column 元素的 sort 属性可以设置该列的初始排序。值选项:

  • ASCENDING - 顺序(0 → 9 → A → Z)。

  • DESCENDING - 倒序(Z → A → 9 → 0)。

示例:

<columns>
    <column id="firstName" property="firstName" sort="DESCENDING"/>
    <column id="lastName" property="lastName"/>
    <column id="city" property="city"/>
    <column id="hobby" property="hobby"/>
</columns>

DataGrid 一次只能按照一列进行排序。如果有多列设置了 sort 属性或者同时设置了 sortsortable="false",界面会报错。

DataGrid 排序事件可以用 SortEvent 跟踪。

分页

dataGrid 可以使用 SimplePagination 简单分页器 组件来提供分页功能:

<dataGrid id="gridPagination"
          width="100%"
          dataContainer="customersDc">
    <columns>
        <column id="firstName" property="firstName"/>
        <column id="lastName" property="lastName"/>
        <column id="hobby" property="hobby"/>
        <column id="age" property="age"/>
    </columns>
    <simplePagination
            itemsPerPageVisible="true"
            itemsPerPageOptions="2, 4, 6"/>
</dataGrid>
data grid pagination

或者,可以使用独立的 Pagination 分页 组件。

DataGrid 操作

DataGrid 组件实现了 ActionsHolder 接口,可以包含自定义操作和 标准列表操作

数据网格的操作定义在内部的 actions 元素中。

如需在 Jmix Studio 中添加 action,可以在界面 XML 或者 Component Hierarchy 面板中选择组件,然后点击 Component Inspector 面板的 Add→Action 按钮。

如果操作有标题,则会显示在数据网格的 右键菜单 中。此外,数据网格的操作可以分配给界面中任意位置的按钮。

可以用 ButtonsPanel 容器在数据网格上方显示操作按钮。

<dataGrid id="gridWithActions"
       width="100%"
       dataContainer="customersDc">
    <actions>
        <action id="create" type="create"/> (1)
        <action id="edit" type="edit"/>
        <action id="remove" type="remove"/>
        <action id="about" caption="Get info"/> (2)
    </actions>
    <columns>
        <column id="age" property="age"/>
        <column id="firstName" property="firstName"/>
        <column id="lastName" property="lastName"/>
    </columns>
    <buttonsPanel id="buttonsActionsPanel" alwaysVisible="true"> (3)
        <button id="create" action="gridWithActions.create"/>
        <button id="edit" action="gridWithActions.edit"/>
        <button id="remove" action="gridWithActions.remove"/>
        <button id="about" action="gridWithActions.about"/>
    </buttonsPanel>
</dataGrid>
1 定义 create 标准操作。
2 定义 about 自定义操作。
3 在数据网格内定义 ButtonsPanel 容器。
data grid actions

列生成

通过 ColumnGeneratorEvent 事件的处理器可以添加动态生成或者动态计算的列,并提供自定义的数据展示。

在渲染数据网格时,事件处理器由框架对每个单元格进行调用。ColumnGeneratorEvent 包含当前行展示的实体信息以及列标识符。

可以用 Studio 创建 ColumnGeneratorEvent 事件的处理器。需选择 Studio 建议的生成类型和对应的 渲染器 类型。

示例,我们生成一个包含 customer 全名称的列。

首先,创建一个 id 为 fullName 的列:

<column id="fullName" caption="Full name">
    <textRenderer nullRepresentation="null"/>
</column>

然后用 Studio 为 fullName 列创建 ColumnGeneratorEvent 处理器并实现如下:

@Install(to = "grid.fullName", subject = "columnGenerator")
private String gridFullNameColumnGenerator(DataGrid.ColumnGeneratorEvent<Customer> columnGeneratorEvent) {
    return columnGeneratorEvent.getItem().getFirstName() + " " + columnGeneratorEvent.getItem().getLastName();
}

结果:

data grid generated column

fullName 列是一个使用 TextRenderer 的生成列。

如需编程式注册列生成处理器,使用 DataGrid 组件的 addGeneratedColumn() 方法。

列值导出

使用 表格导出 扩展组件可以将 DataGrid 组件的内容导出成支持的文件类型。

安装完组件后,可以为数据网格定义 excelExportjsonExport 操作,也可使用 表格导出器

下面的例子中,在使用 excelExport 操作导出 XLSX 文件时,为数据的某一列设置了自定义的输出。

界面描述:

<dataGrid id="gridExport"
          width="100%"
          dataContainer="customersDc">
    <actions>
        <action id="excelExport" type="excelExport"/>
    </actions>
    <columns>
        <column id="firstName" property="firstName"/>
        <column id="hobby" property="hobby"/>
        <column id="age" property="age"/>
    </columns>
</dataGrid>

界面控制器:

@Named("gridExport.excelExport")
protected ExcelExportAction gridExcelExport;

@Subscribe
public void onInit(InitEvent event) {
    gridExcelExport.addColumnValueProvider("firstName", context -> { (1)
        Customer customer = context.getEntity();
        return "Name: " + customer.getFirstName();
    });
}
1 该方法一个参数是列标识符,第二个是从列中获取展示值的函数。

渲染器

数据在列中的显示方式可以使用带参数的渲染器(renderer)以声明式的方法自定义。一些 DataGrid 的渲染器通过特定的 XML 元素设置,并且定义了对应的属性作为参数。对常规列和生成列,都可以声明渲染器。

框架提供下列渲染器:

ButtonRenderer

ButtonRenderer 渲染为按钮,按钮标题为字符串。

ButtonRenderer 不能在 XML 描述中定义,因为没有办法在 XML 描述中定义渲染器点击监听器。Studio 会在界面控制器的 onInit() 中生成 ButtonRenderer 的声明代码,示例:

@Autowired
private Notifications notifications;

@Autowired
private DataGrid<Customer> gridButtonRenderer;


@Subscribe
public void onInit(InitEvent event) {
    DataGrid.ButtonRenderer<Customer> gridButtonRendererFirstNameRenderer =
            getApplicationContext().getBean(DataGrid.ButtonRenderer.class);
    gridButtonRendererFirstNameRenderer.setRendererClickListener(
            clickableRendererClickEvent ->
            notifications.create()
                    .withCaption("ButtonRenderer")
                    .withDescription("Column id: " +
                            clickableRendererClickEvent.getColumnId())
                    .show());
    gridButtonRenderer.getColumn("firstName")
            .setRenderer(gridButtonRendererFirstNameRenderer);
}
data grid button renderer

ImageRenderer

  • ImageRenderer 是一个展示图片的渲染器。对应属性的值用作图片地址,地址可以是主题资源或 URL。

ImageRenderer 不能在 XML 描述中定义,因为没有办法在 XML 描述中定义渲染器点击监听器。Studio 会在界面控制器的 onInit() 中生成 ImageRenderer 的声明代码,示例:

@Autowired
private Notifications notifications;

@Autowired
private DataGrid<Country> imageGrid;


@Subscribe
public void onInit(InitEvent event) {
    DataGrid.ImageRenderer<Country> imageGridFlagRenderer =
            getApplicationContext().getBean(DataGrid.ImageRenderer.class);
    imageGridFlagRenderer.setRendererClickListener(clickableTextRendererClickEvent -> {
    });
    imageGrid.getColumn("flag").setRenderer(imageGridFlagRenderer);
}

结果:

data grid image renderer

CheckBoxRenderer

CheckBoxRenderer 将布尔值作为复选框展示。

DataGridcolumn 元素有一个子元素 checkBoxRenderer

<column id="isEmail" caption="Is email">
    <checkBoxRenderer/>
</column>

NumberRenderer

NumberRenderer 按照定义的格式展示数字。

DataGridcolumn 元素有一个子元素 numberRenderernumberRenderer 有可选属性 nullRepresentation 和必需字符串属性 format

<column id="percent" property="percent">
    <numberRenderer nullRepresentation="null" format="%d%%"/>
</column>

formatString 是描述数字格式的字符串,用来创建 NumberFormat 实例。

LocalDateTimeRenderer

LocalDateTimeRendererLocalDateTime 值展示日期时间。

DataGridcolumn 元素有子元素 localDateTimeRenderer,该元素有非必要的 nullRepresentation 属性和必填的 format 字符串属性:

<column id="dateTime" property="dateTime">
    <localDateTimeRenderer nullRepresentation="null"
                           format="dd/MM/YYYY HH:mm:ss"/>
</column>

LocalDateRenderer

LocalDateRendererLocalDate 值显示日期。

DataGridcolumn 元素有子元素 localDateRenderer,该元素有非必要的 nullRepresentation 属性和必填的 format 字符串属性:

<column id="date" property="date">
    <localDateRenderer nullRepresentation="null" format="dd/MM/YYYY"/>
</column>

结果:

data grid local date renderer

DateRenderer

DateRenderer 用定义的格式显示日期。

DataGridcolumn 元素有子元素 dateRenderer,该元素有非必要的 nullRepresentation 属性和必填的 format 字符串属性:

<dataGrid id="eventGrid"
          width="100%"
          dataContainer="eventsDc">
    <columns>
        <column id="name" property="name"/>
        <column id="startDate" property="startDate">
            <dateRenderer nullRepresentation="null"
                          format="yyyy-MM-dd HH:mm:ss"/>
        </column>
        <column id="endDate" property="endDate"/>
    </columns>
</dataGrid>

formatString 是描述日期时间格式的字符串,用来创建 DateFormat 实例。

data grid date renderer

注意这里 startDate 字段有 DateRenderer,而 endDate 没有。

ProgressBarRenderer

ProgressBarRenderer 将 0~1 之间 double 类型的值展示为 ProgressBar 组件。

DataGridcolumn 元素有子元素 progressBarRenderer

<column id="percent" property="percent">
    <progressBarRenderer/>
</column>

结果:

data grid progress bar renderer

HtmlRenderer

HtmlRenderer 展示 HTML。

DataGridcolumn 元素有子元素 htmlRenderer,该元素有非必要的属性 nullRepresentation

下面是渲染 endDate 属性的示例:

<column id="endDate" property="endDate">
    <htmlRenderer nullRepresentation="null"/>
</column>
@Install(to = "htmlGrid.endDate", subject = "columnGenerator")
private String htmlGridEndDateColumnGenerator(DataGrid.ColumnGeneratorEvent<Event> columnGeneratorEvent) {
    return columnGeneratorEvent.getItem().getEndDate().before(new Date())
            ? "<font color='red'>" +
            columnGeneratorEvent.getItem().getEndDate() + "</font>"
            : "<font color='green'>" +
            columnGeneratorEvent.getItem().getEndDate() + "</font>";
}

结果:

data grid html renderer

ClickableTextRenderer

ClickableTextRenderer 将普通的文本字符串显示为链接,并有点击事件处理器。

ClickableTextRenderer 不能在 XML 描述中定义,因为没有办法在 XML 描述中定义渲染器点击监听器。Studio 会在界面控制器的 onInit() 中生成 ClickableTextRenderer 的声明代码,示例:

@Autowired
private Notifications notifications;

@Autowired
private DataGrid<Customer> gridClick;


@Subscribe
public void onInit(InitEvent event) {
    DataGrid.ClickableTextRenderer<Customer> gridClickFirstNameRenderer =
            getApplicationContext().getBean(DataGrid.ClickableTextRenderer.class);
    gridClickFirstNameRenderer.setRendererClickListener(clickEvent ->
            notifications.create()
                    .withDescription("The full name: " +
                            clickEvent.getItem().getFirstName() +
                            " " + clickEvent.getItem().getLastName())
                    .show());
    gridClick.getColumn("firstName").setRenderer(gridClickFirstNameRenderer);
}

结果:

data grid click text renderer

TextRenderer

TextRenderer 展示简单的字符串。

DataGridcolumn 元素有子元素 textRenderer,该元素有非必要的 nullRepresentation 属性:

<column id="fullName" caption="Full name">
    <textRenderer nullRepresentation="null"/>
</column>

ComponentRenderer

ComponentRenderer UI 组件渲染器。

DataGridcolumn 元素有子元素 componentRenderer

<column id="age" property="age">
    <componentRenderer/>
</column>

下面的例子中,我们用 Slider 组件展示 age 的值。

@Install(to = "gridComponent.age", subject = "columnGenerator")
private Component gridComponentAgeColumnGenerator(DataGrid.ColumnGeneratorEvent<Customer> columnGeneratorEvent) {
    Slider<Integer> slider = uiComponents.create(Slider.NAME);
    slider.setValue(columnGeneratorEvent.getItem().getAge());
    slider.setEditable(false);
    slider.setWidth("150px");
    return slider;
}

结果:

data grid component renderer

IconRenderer

IconRenderer 展示 JmixIcon 的渲染器。

DataGridcolumn 元素有子元素 iconRenderer

下面示例,渲染生成的 hasEmail 属性:

<column id="hasEmail" caption="Has id">
    <iconRenderer/>
</column>
@Install(to = "iconGrid.hasEmail", subject = "columnGenerator")
private Icons.Icon iconGridHasEmailColumnGenerator(DataGrid.ColumnGeneratorEvent<Customer> columnGeneratorEvent) {
    return columnGeneratorEvent.getItem().getEmail() != null ?
            JmixIcon.OK : JmixIcon.EXCLAMATION_TRIANGLE;
}

结果:

data grid icon renderer

数据详情生成器

数据详情是在可展开的区域展示特定行的更多信息。

DetailsGenerator 接口支持创建自定义组件展示数据详情。

用 Studio 可以为 DataGrid 创建 DetailsGenerator` 处理器(参阅 处理器)并实现:

@Autowired
private DataGrid<Customer> detailsGrid;

@Install(to = "detailsGrid", subject = "detailsGenerator")
private Component detailsGridDetailsGenerator(Customer customer) {
    VBoxLayout mainLayout = uiComponents.create(VBoxLayout.class);
    mainLayout.setWidth("100%");
    mainLayout.setMargin(true);

    HBoxLayout headerBox = uiComponents.create(HBoxLayout.class);
    headerBox.setWidth("100%");

    Label<String> infoLabel = uiComponents.create(Label.TYPE_STRING);
    infoLabel.setHtmlEnabled(true);
    infoLabel.setStyleName("h1");
    infoLabel.setValue("Customer info:");

    Component closeButton = createCloseButton(customer);
    headerBox.add(infoLabel);
    headerBox.add(closeButton);
    headerBox.expand(infoLabel);

    Component content = getContent(customer);

    mainLayout.add(headerBox);
    mainLayout.add(content);
    mainLayout.expand(content);

    return mainLayout;
}

protected Component createCloseButton(Customer entity) {
    Button closeButton = uiComponents.create(Button.class);
    closeButton.setIcon("font-icon:TIMES");
    BaseAction closeAction = new BaseAction("closeAction")
            .withHandler(actionPerformedEvent ->
                    detailsGrid.setDetailsVisible(entity, false))
            .withCaption("");
    closeButton.setAction(closeAction);
    return closeButton;
}

protected Component getContent(Customer entity) {
    Label<String> content = uiComponents.create(Label.TYPE_STRING);
    content.setHtmlEnabled(true);
    content.setId("contentLabel");

    StringBuilder sb = new StringBuilder();
    sb.append("<b>Full name</b><br>")
            .append(entity.getFirstName() + " " + entity.getLastName() + "<br><br>")
            .append("<b>Country</b><br>")
            .append(entity.getCity().getCountry().getName()+ "<br><br>")
            .append("<b>City</b><br>")
            .append(entity.getCity().getName());

    content.setValue(sb.toString());

    return content;
}

如需手动处理显示和隐藏,需要自己实现监听器。下面的例子中,我们在 DataGrid 组件的 ItemClickAction 中打开详情:

@Subscribe("detailsGrid")
public void onDetailsGridItemClick(DataGrid.ItemClickEvent<Customer> event) {
    detailsGrid.setItemClickAction(new BaseAction("itemClickAction")
            .withHandler(actionPerformedEvent ->
                    detailsGrid.setDetailsVisible(detailsGrid.getSingleSelected(), true)));
}

结果:

data grid detail generator

编辑

DataGrid 组件支持行内编辑器来编辑单元格数据。设置 editorEnabled 属性为 true 启用行内编辑。

使用方法

  • 双击需要编辑的字段或选中可编辑单元格后按下回车都可以开启行内编辑。

  • 带有 editable = true 属性的列展示实体属性编辑的组件。不可编辑字段则无法使用。

  • 每个可编辑列的编辑组件是根据对应实体的属性自动选取的。例如,对于字符串和数字类型的属性,使用 TextField,日期时间 - DateField,枚举 - ComboBox,实体引用 - EntityPicker

  • 当一行处于编辑模式时,会显示默认的 OKCancel 按钮,用户可提交或取消编辑。

  • 如需保存修改并退出编辑模式,点击 OK 按钮或按回车。

  • 如需放弃修改并退出编辑模式,点击 Cancel 按钮或按 Esc

data grid editing

模式

编辑模式分为缓存模式和非缓存模式。

  • 缓存模式编辑,数据的修改必须显式提交。

  • 非缓存模式编辑,当字段失去焦点时自动提交。

editorBuffered 属性用于设置缓存编辑模式。默认为 true

此时,数据的修改只是提交至数据容器。数据保存至数据库的过程通常在界面提交时由 数据上下文 完成。

自定义行内编辑器

editorCancelCaption 属性设置 DataGrid 组件编辑器中取消(cancel)按钮的标题。

editorSaveCaption 属性设置 DataGrid 组件编辑器中保存(save)按钮的标题。

可以通过 EditorFieldGenerationContext 类对编辑器组件进行定制。

DataGrid 编辑模式中创建一个编辑字段生成器,用于为该列生成编辑组件,示例:

@Autowired
private CollectionContainer<City> citiesDc;

@Install(to = "editingGrid.city", subject = "editFieldGenerator")
private Field<City> editingGridCityEditFieldGenerator(
        DataGrid.EditorFieldGenerationContext<Customer> editorFieldGenerationContext) {
    ComboBox<City> comboBox = uiComponents.create(ComboBox.NAME);
    comboBox.setValueSource((ValueSource<City>) editorFieldGenerationContext
            .getValueSourceProvider().getValueSource("city"));
    comboBox.setOptionsList(citiesDc.getItems());
    return comboBox;
}

结果:

data grid edit field generator

还有通过声明式的方式定义使用何种组件选择实体的通用机制。基于 jmix.ui.component.entity-field-typejmix.ui.component.entity-field-actions 属性。

例如,可以在 application.properties 文件中增加下面的内容:

jmix.ui.component.entity-field-type.uiex1_City = entityComboBox
jmix.ui.component.entity-field-actions.uiex1_City = entity_lookup, entity_open, entity_clear

然后,会自动为 City 实体生成 EntityComboBox 组件:

data grid editable custom field

跨字段验证

DataGrid 的行内编辑器也可以考虑实体约束(跨字段验证)。如果有验证错误,DataGrid 会展示错误消息。用 editorCrossFieldValidate 属性启用/禁用验证。默认值为 true

编辑器事件

编辑器事件提供对其使用组件的访问,支持修改或使用组件值。

  • EditorOpenEventDataGrid 编辑器打开之前发送的事件。

    示例:

    @Subscribe("editEventsGrid")
    public void onEditEventsGridEditorOpen(DataGrid.EditorOpenEvent<Event> event) {
        Map<String, Field> fields = event.getFields();
        Field field1 = fields.get("startDate");
        Field field2 = fields.get("endDate");
        field1.addValueChangeListener(valueChangeEvent ->
                field2.setValue(DateUtils.addDays((Date) field1.getValue(), 1)));
        field2.addValueChangeListener(valueChangeEvent ->
                field1.setValue(DateUtils.addDays((Date) field2.getValue(), -1)));
    }
  • EditorCloseEventDataGrid 编辑器关闭之后发送的事件。

  • EditorPreCommitEvent 在数据更新之前发送的事件。

  • EditorPostCommitEvent 在数据更新之后发送的事件。

行内编辑器方法

  • getEditedItem() 返回正在被编辑的数据项,或 null,如果此时无数据正在编辑。

  • isEditorActive() 是否正在编辑某个数据项。

  • edit() 方法为指定实体打开编辑接口。如果数据项在当前界面区域不可见,DataGrid 会将数据项滚动到可视区域。

右键菜单

在数据网格中点击右键可以激活右键菜单。

contextMenuEnabled 属性可以开启右键菜单。默认值为 true。右键菜单展示 数据网格操作(如果有的话)。

右键点击的事件可以通过 ContextClickEvent 跟踪。

样式

可以在界面 XML 或者控制器中使用 stylename 属性为 DataGrid 组件设置预定义样式:

<dataGrid id="progressGrid"
          width="100%"
          stylename="no-stripes"
          dataContainer="budgetItemsDc">
progressGrid.setStyleName(ThemeClassNames.DATAGRID_NO_STRIPES);

预定义样式:

  • borderless - 删除数据网格外边框;

  • no-horizontal-lines - 删除行之间的水平分隔线;

  • no-vertical-lines - 删除列之间的垂直分隔线;

  • no-stripes - 删除行间隔背景色。

DataGrid 的外观可以使用带 $jmix-datagrid-* 前缀的 SCSS 变量进行自定义。在创建了 自定义主题 之后,可以在可视化编辑器中修改这些变量。

也可以参阅 RowStyleProvider

DataGrid 接口方法

  • getColumns() 按当前界面的展示顺序获取列集合。

  • getVisibleColumns() 获取有权限查看的所有列集合。

  • sort() 方法对 DataGrid 的数据按照所选择的列和排序规则进行 排序

  • scrollTo()DataGrid 滚动到指定行。需要一个实体实例作为输入参数来指定滚动到哪一行。除了实体实例参数,另有重载方法支持 ScrollDestination 参数,该参数可以为以下值:

    • ANY - 滚动尽量少的位置来展示所目标元素。如果元素在视图内,则根据当前滚动条的位置,此配置与 STARTEND 相同。如果元素不在视图内,则与 START 相同。

    • START - 使所需要的数据展示在可见部分的顶端。当前视图不会滚动超过所见内容的距离。

    • MIDDLE - 使所需要的数据展示在可见部分的中部。当前视图不会滚动超过所见内容的距离,即使数据比当前视图能展示的范围还要大。且视图不会滚动超过第一个元素。

    • END - 使所需要的数据展示在可见部分的底部。视图不会滚动超过第一个元素。

  • scrollToStart()scrollToEnd() - 分别滚动 DataGrid 至第一个和最后一个数据项。

  • The getAggregationResults() 方法返回 聚合 结果的 map,key 为 DataGrid 的列标识符,value 为聚合值。

事件和处理器

如需使用 Jmix Studio 生成处理器的桩代码,需要在界面 XML 描述或者 Jmix UI 层级结构面板选中该组件,然后用 Jmix UI 组件面板的 Handlers 标签页生成。

或者可以使用界面控制器顶部面板的 Generate Handler 按钮。

ColumnCollapsingChangeEvent

ColumnCollapsingChangeEvent 当列的 折叠 状态发生改变时发送。

下面示例为 XML 中 id 是 collapseGrid 的数据网格订阅了该事件:

@Subscribe("collapseGrid")
public void onCollapseGridColumnCollapsingChange(DataGrid.ColumnCollapsingChangeEvent event) {
    notifications.create()
            .withCaption((event.isCollapsed() ? "Collapsed: " : "Expanded: ") +
                    event.getColumn().getCaption())
            .show();
}

如需以编程的方式注册事件处理器,使用组件的 addColumnCollapsingChangeListener() 方法。

ColumnReorderEvent

参阅 ColumnReorderEvent

ColumnResizeEvent

ColumnResizeEvent 当列发生 大小改变 时发送。

下面示例为 XML 中 id 是 resizedEventGrid 的数据网格订阅了该事件:

@Subscribe("resizedEventGrid")
public void onResizedEventGridColumnResize(DataGrid.ColumnResizeEvent event) {
    notifications.create()
            .withCaption("The " + event.getColumn().getCaption() + " column was resized")
            .show();
}

如需以编程的方式注册事件处理器,使用组件的 addColumnResizeListener() 方法。

ContextClickEvent

ContextClickEvent 当在 DataGrid 内点击鼠标右键时发送。

ContextClickEvent 包含鼠标事件的详细信息。

下面示例为 XML 中 id 是 contextGrid 的数据网格订阅了该事件:

@Subscribe("contextGrid")
public void onContextGridContextClick(DataGrid.ContextClickEvent event) {
    notifications.create()
            .withCaption("Clicked " + event.getButton().name())
            .show();
}

如需以编程的方式注册事件处理器,使用组件的 addContextClickListener() 方法。

EditorCloseEvent

参阅 EditorCloseEvent

EditorOpenEvent

参阅 EditorOpenEvent

EditorPostCommitEvent

EditorPreCommitEvent

ItemClickEvent

ItemClickEvent 当用户点击数据网格时发送。ItemClickEvent 包含下列信息:

  • 鼠标事件详情;

  • 点击行的实体实例;

  • 数据项的 id

  • 点击列的 id

下面的示例,当用户单击可编辑单元格时,让用户开始 编辑

@Subscribe("clickGrid")
public void onClickGridItemClick(DataGrid.ItemClickEvent<Event> event) {
    clickGrid.edit(event.getItem());
}

如需以编程的方式注册事件处理器,使用组件的 addItemClickListener() 方法。

SelectionEvent

参阅 SelectionEvent

SortEvent

SortEvent 当数据发生 排序 变化时发送。

下面示例为 XML 中 id 是 sortGrid 的数据网格订阅了该事件:

@Subscribe("sortGrid")
public void onSortGridSort(DataGrid.SortEvent event) {
    notifications.create()
            .withCaption("The sort order was changed")
            .show();
}

如需以编程的方式注册事件处理器,使用组件的 addSortListener() 方法。

ContextHelpIconClickHandler

DetailsGenerator

参阅 DetailsGenerator

LookupSelectHandler

RowDescriptionProvider

RowDescriptionProvider 当用户鼠标悬停在 DataGrid 某行时,展示行的描述信息(提示框)。如果为某列也设置了 DescriptionProvider,那么,由 RowDescriptionProvider 生成的行描述信息只为那些单元格描述 provider 返回 null 的单元格展示。

下面的示例中,我们展示 rowDescGridRowDescriptionProvider 用法:

@Install(to = "rowDescGrid", subject = "rowDescriptionProvider")
private String rowDescGridRowDescriptionProvider(Customer customer) {
    switch (customer.getLevel()) {
        case SILVER:
            return "The customer gets special offers from sellers and birthday congratulations";
        case GOLD:
            return "The customer gets 2 coupons with a rating of $ 1";
        case PLATINUM:
            return "The customer gets a coupons with a par value of $ 3";
        case DIAMOND:
            return "The customer gets a coupon with a par value of $ 5";
    }
    return null;
}

结果:

data grid row description

如需以编程的方式注册 provider,使用组件的 setRowDescriptionProvider() 方法。

RowStyleProvider

RowStyleProvider 支持 DataGrid 行级的样式 provider。 DataGrid 可以使用多个 providers 获取行的样式名。

设置样式示例:

@Install(to = "styledGrid", subject = "rowStyleProvider")
private String styledGridRowStyleProvider(Customer customer) {
    switch (customer.getLevel()) {
        case SILVER:
            return "level-silver";
        case GOLD:
            return "level-gold";
        case PLATINUM:
            return "level-platinum";
        case DIAMOND:
            return "level-diamond";
    }
    return null;
}

然后需要在应用程序主题中定义行样式。创建主题的详细信息请参阅 主题。CSS 选择器中需要使用界面控制器中 provider 返回的样式名。示例:

.v-grid-row.level-silver > td {
  background-color: #f2f2f2;
  color: black;
}

.v-grid-row.level-gold > td {
    background-color: #ffda79;
    color: black;
}

.v-grid-row.level-platinum > td {
      background-color: #637497;
      color: black;
}

.v-grid-row.level-diamond > td {
        background-color: #8befff;
        color: black;
}

结果:

data grid row style

XML 属性