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

时间:2016-09-05 13:12:03

标签: excel apache-poi

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

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

我试图在许多工作簿中重现这种行为 - 这是针对另一个工作簿中的一系列值验证一个单元格。我们同时使用HSSF和XSSF,但首选XSSF。

如果我传递字符串*文件:/ t:/Excel/pkTable.xlsx#'pkTable'!$ A $ 3:$ A $ * 6到XSSFDataValidationHelper.createCustomConstraint()POI不会抛出异常。但是在Excel中打开时,我们看到消息'我们发现某些内容存在问题... '

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

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

第二种方法是使用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 ”)将创建一个良好的工作跨工作簿超链接。 (注意:与Excel Visual Basic中显示的公式相同的公式将扩展通过Java提供的#字符。)

对于工作超链接,必须使用CreationHelper.createHyperlink(Hyperlink.LINK_URL)代替CreationHelper.createHyperlink(Hyperlink.LINK_DOCUMENT)创建超链接;这表明对于范围检查情况,内部POI假设LINK_DOCUMENT语法,即使传递的字符串是LINK_URL。 有谁知道怎么打败这个?

通常,所有这些聪明的技术(超链接/范围检查)似乎最终作为Excel公式插入。在此基础上,本机Excel Visual Basic中可用的每种技术都应通过POI提供

2 个答案:

答案 0 :(得分:2)

要从不同的工作簿制作Excel数据验证下拉列表工作,必须注意多个事项。所有使用的列表范围必须是命名范围。但最烦人的事情是用户必须打开两个工作簿 - 带有下拉列表的工作簿,以及带有原始源列表的工作簿。

因此,数据验证约束不能是对文件路径的引用。它必须是对随机存取存储器中的对象的引用。如果它是对命名范围的引用,则此命名范围也不能是对文件路径的引用。它还必须是对随机存取存储器中的对象的引用。

但是也可以使用apache poi:

toString()

所有外部引用的路径都是相对的。因此,在此示例中,需要在同一目录中创建两个工作簿。

您应首先打开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) { } } } DatavalidationSource.xlsx中有一个名为SourceList的列表,A1:A5中有Datavalidation.xlsx的链接。在B1中,下拉列表位于Datavalidation.xlsx。数据验证列表引用了引用Sheet1!B2的名称DVList

DatavalidationSource.xlsx!SourceList中是使用Datavalidation.xlsx#Sheet1!K1的链接。

答案 1 :(得分:0)

感谢您在创建此示例方面的辛勤工作。 非常感谢 - 人们可以做的事情真是太神奇了。

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

请参阅Is there a max number items while generating drop down list in Excel using Apache POI?

我的解决方案的一大缺点是我的用户无法添加或修改主数据(例如:添加新颜色或国家/地区代码)。因为它不是一个用例,我需要支持。 (我很幸运)此外,对我来说,使用隐藏或显式参考数据的最大优点是我可以支持强制执行复合自然键 - 在某些情况下,外键引用实际上是一些pk值的字符串连接(我没有设计这个数据库!!)