在POI中的工作表之间设置范围validation

我们的程序将一些数据库数据导出为Excel。 用户可以select:1)一个工作簿和多个选项卡,每个原始表格一个。 2)多个工作簿(文件),每个只有一个选项卡。

我完全按照标准POI文档( https://poi.apache.org/spreadsheet/quick-guide.html#Validation )中的说明为外键/主键引用添加validation码。 神奇的公式与'pkTable'类似!$ A $ 3:$ A $ 6。

我正在尝试在许多工作簿中重现此行为 – 即在另一个工作簿中validation一个单元格的值。 我们使用HSSF和XSSF,但是XSSF是首选。

如果将string* file:/ t:/Excel/pkTable.xlsx#'pkTable'!$ A $ 3:$ A $ * 6传递给XSSFDataValidationHelper.createCustomConstraint(),则POI不会引发exception。 但在Excel中打开时,我们看到“ 我们发现某些内容存在问题…

我也尝试通过创build命名范围间接解决问题。 那么我们需要做的就是指出一个命名的范围在不同的工作簿中。 我已经尝试了两种方法:

如果我传入file:/ t:/Excel/pkTable.xlsx#'pkTable'!$ A $ 3:$ A $ 6而不是' A $ 6 ',则尝试直接限定命名范围 (即在名为Range.setRefersToFormula pkTable'!$ A $ 3:$ A $ 6我们得到一个POI预期'org.apache.poi.ss.formula.FormulaParseException: 指定的命名范围'文件'在当前工作簿中不存在。

第二种方法是使用namedRange.setRefersToFormula(“pkTable'!$ A $ 3:$ A $ 6”)并尝试将调用限定在指定的范围 – 即调用XSSFDataValidationHelper.createCustomConstraint(“ file:/ t:/ Excel / pkTable.xlsx#Rg_pkTable“ )(其中'Rg_pkTable'是我的范围名称)POI很高兴 – 但Excel告诉我' 我们发现一些内容的问题 '

这是令人沮丧的,因为我的实验显示:theCell.setHyperlink(“ file:/ t:/Excel/pkTable.xlsx#'pkTable'!A1 ”)将创build一个良好的工作跨工作簿超链接。 (注意:与Excel Visual Basic中出现的公式相同的公式将扩展通过Java提供的#字符。)

对于正在运行的超链接,必须使用CreationHelper.createHyperlink(Hyperlink.LINK_URL)来代替CreationHelper.createHyperlink(Hyperlink.LINK_DOCUMENT)来创build超链接。 这表明,对于范围检查案例,即使传递的string是LINK_URL,内部POI也会采用LINK_DOCUMENT语法。 有谁知道如何击败这个?

一般来说,所有这些巧妙的技术(超链接/范围检查)似乎最终插入为Excel公式。 在此基础上,本地Excel Visual Basic中可用的每种技术均应通过POI提供

要使Excel数据validation下拉列表工作在不同的工作簿中,必须注意多个事项。 所有使用的列表范围必须是命名范围。 但最令人讨厌的是, 用户将不得不同时打开两个工作簿 – 一个是下拉列表,另一个是原始源列表。

所以数据validation约束不能是对文件path的引用。 它必须是对随机存取存储器中的对象的引用。 如果它是对一个命名范围的引用,那么这个命名范围也不能是对文件path的引用。 它也必须是对随机存取存储器中的对象的引用。

但是也可以使用apache poi:

import java.io.*; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.*; import org.apache.poi.ss.util.CellRangeAddressList; class DataValidationExternalWorkbook { public static void main(String[] args) { try { Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("Sheet1"); for (int i = 0; i < 5; i++) { sheet.createRow(i).createCell(0).setCellValue("ListItem " + i); } Name namedCell = workbook.createName(); namedCell.setNameName("SourceList"); String reference = "Sheet1!$A$1:$A$5"; namedCell.setRefersToFormula(reference); Cell cell = sheet.getRow(0).createCell(1); Hyperlink fileLink = workbook.getCreationHelper().createHyperlink(Hyperlink.LINK_FILE); fileLink.setAddress("Datavalidation.xlsx#Sheet1!B2"); cell.setCellValue("Click here to Datavalidation.xlsx, Sheet1, B2"); cell.setHyperlink(fileLink); FileOutputStream fileOut = new FileOutputStream("/home/axel/Dokumente/DatavalidationSource.xlsx"); workbook.write(fileOut); fileOut.close(); workbook = new XSSFWorkbook(); sheet = workbook.createSheet("Sheet1"); namedCell = workbook.createName(); namedCell.setNameName("DVList"); reference = "DatavalidationSource.xlsx!SourceList"; namedCell.setRefersToFormula(reference); DataValidationHelper dvHelper = sheet.getDataValidationHelper(); DataValidationConstraint dvConstraint = dvHelper.createFormulaListConstraint("DVList"); CellRangeAddressList addressList = new CellRangeAddressList(1, 1, 1, 1); DataValidation validation = dvHelper.createValidation(dvConstraint, addressList); sheet.addValidationData(validation); cell = sheet.createRow(0).createCell(1); fileLink = workbook.getCreationHelper().createHyperlink(Hyperlink.LINK_FILE); fileLink.setAddress("DatavalidationSource.xlsx#Sheet1!B1"); cell.setCellValue("Open DatavalidationSource.xlsx to make the list in B2 work."); cell.setHyperlink(fileLink); cell = sheet.getRow(0).createCell(10); fileLink = workbook.getCreationHelper().createHyperlink(Hyperlink.LINK_DOCUMENT); fileLink.setAddress("Sheet2!B2:D4"); cell.setCellValue("goto Sheet2!B2:D4"); cell.setHyperlink(fileLink); sheet = workbook.createSheet("Sheet2"); fileOut = new FileOutputStream("/home/axel/Dokumente/Datavalidation.xlsx"); workbook.write(fileOut); fileOut.close(); } catch (FileNotFoundException fnfex) { } catch (IOException ioex) { } } } 

所有外部引用的path都是相对的。 所以在这个例子中,两个工作簿都需要在同一个目录中创build。

您应该首先打开DatavalidationSource.xlsx 。 在A1:A5有名为SourceList的列表, Datavalidation.xlsxB1有一个到Datavalidation.xlsx的链接。 在Datavalidation.xlsx ,下拉列表在Sheet1!B2 。 数据validation列表引用了名称DVList ,它是指DatavalidationSource.xlsx!SourceList

Datavalidation.xlsx#Sheet1!K1是使用Hyperlink.LINK_DOCUMENT

感谢您在创build这个示例方面所做的努力。 非常感谢 – 这是可以做的很神奇。

我实际上是通过“ 作弊 ”并在每个工作簿中创build数据的副本来做到的。 如果所有可能的pk值的逗号分隔列表的长度小于256,那么我创build一个明确的string列表。 否则,我引用包含复制值的隐藏选项卡上的列。 因为我的fk列的标题包含一个工作超链接到另一个工作簿,用户体验是好的。 用户发现这些值在修改时受到限制 – 但是他们也可以单击并立即导航到另一个工作簿的“预期位置”中的值列表。

请参见使用Apache POI在Excel中生成下拉列表时是否有最大数量的项目?

我的解决scheme的一大缺点是我的用户不能添加或修改主数据(例如:添加一个新的颜色或国家代码)。 碰巧不是我需要支持的用例。 (我只是幸运的)另外,对我来说,使用隐藏或显式引用数据的最大好处是我可以支持复合自然键的 fk实现 – 在某些情况下,外键引用实际上是一个数字的string连接PK值(我没有devise这个数据库!!)