leo
leo

Reputation: 51

Need help adding OpenXML dataValidation to existing Excel file

I need to modify an Excel file with a dropdown list using OpenXML dataValidation.

For some reason, I can create the file with the dropdown but I can't add 1 using Open() method.

The result is an empty excel file without dropdown.

This is the method I used to create the file:

public static void addCreateValidator(string path)
{
    using (SpreadsheetDocument myDoc = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Workbook))
    {

        WorkbookPart workbookpart = myDoc.AddWorkbookPart();
        workbookpart.Workbook = new Workbook();

        WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
        SheetData sheetData = new SheetData();

        worksheetPart.Worksheet = new Worksheet(sheetData);
        Sheets sheets = myDoc.WorkbookPart.Workbook.AppendChild(new Sheets());
        sheets.AppendChild(new Sheet()
        {
            Id = myDoc.WorkbookPart.GetIdOfPart(myDoc.WorkbookPart.WorksheetParts.First()),
            SheetId = 1,
            Name = "Sheet1"
        });

        DataValidations dataValidations = new DataValidations();
        DataValidation dataValidation = new DataValidation()
        {
            Type = DataValidationValues.List,
            AllowBlank = true,
            SequenceOfReferences = new ListValue<StringValue>() { InnerText = "A1:A1048576" }
        };
        Formula1 formula = new Formula1();
        formula.Text = "\"FirstChoice,SecondChoice,ThirdChoice\"";

        dataValidation.Append(formula);
        dataValidations.Append(dataValidation);

        worksheetPart.Worksheet.AppendChild(dataValidations);
    }
}

This is the method used to modify the file:

public static void addValidation(string filepath)
    {
            using (SpreadsheetDocument myDoc = SpreadsheetDocument.Open(filepath, true))
            {

            WorkbookPart wbPart = myDoc.WorkbookPart;

            var sheetData = new DocumentFormat.OpenXml.Spreadsheet.SheetData();
            WorksheetPart worksheetPart =
                      GetWorksheetPartByName(myDoc, "Sheet1");

            DataValidations dataValidations = new DataValidations();
            DataValidation dataValidation = new DataValidation()
            {
                Type = DataValidationValues.List,
                AllowBlank = true,
                SequenceOfReferences = new ListValue<StringValue>() { InnerText = "h1" }
            };
            Formula1 formula = new Formula1();
            formula.Text = "\"FirstChoice,SecondChoice,ThirdChoice\"";

            dataValidation.Append(formula);
            dataValidations.Append(dataValidation);

            worksheetPart.Worksheet.AppendChild(dataValidations);
            //worksheetPart.Worksheet.Save();
        }


    }

Can you see any error or suggest a workaround?

Upvotes: 2

Views: 1456

Answers (1)

petelids
petelids

Reputation: 12815

This is because in the existing file you have some elements that you don't have when you create the file from scratch.

The schema for OpenXml files quite often uses sequences so the order of elements is important. In this case the problem is in your Worksheet where the schema is:

<xsd:complexType name="CT_Worksheet">
  <xsd:sequence>
    <xsd:element name="sheetPr" type="CT_SheetPr" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="dimension" type="CT_SheetDimension" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="sheetViews" type="CT_SheetViews" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="sheetFormatPr" type="CT_SheetFormatPr" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="cols" type="CT_Cols" minOccurs="0" maxOccurs="unbounded"/>
    <xsd:element name="sheetData" type="CT_SheetData" minOccurs="1" maxOccurs="1"/>
    <xsd:element name="sheetCalcPr" type="CT_SheetCalcPr" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="sheetProtection" type="CT_SheetProtection" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="protectedRanges" type="CT_ProtectedRanges" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="scenarios" type="CT_Scenarios" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="autoFilter" type="CT_AutoFilter" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="sortState" type="CT_SortState" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="dataConsolidate" type="CT_DataConsolidate" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="customSheetViews" type="CT_CustomSheetViews" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="mergeCells" type="CT_MergeCells" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="phoneticPr" type="CT_PhoneticPr" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="conditionalFormatting" type="CT_ConditionalFormatting" minOccurs="0" maxOccurs="unbounded"/>
    <xsd:element name="dataValidations" type="CT_DataValidations" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="hyperlinks" type="CT_Hyperlinks" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="printOptions" type="CT_PrintOptions" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="pageMargins" type="CT_PageMargins" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="pageSetup" type="CT_PageSetup" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="headerFooter" type="CT_HeaderFooter" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="rowBreaks" type="CT_PageBreak" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="colBreaks" type="CT_PageBreak" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="customProperties" type="CT_CustomProperties" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="cellWatches" type="CT_CellWatches" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="ignoredErrors" type="CT_IgnoredErrors" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="smartTags" type="CT_SmartTags" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="drawing" type="CT_Drawing" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="drawingHF" type="CT_DrawingHF" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="picture" type="CT_SheetBackgroundPicture" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="oleObjects" type="CT_OleObjects" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="controls" type="CT_Controls" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="webPublishItems" type="CT_WebPublishItems" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="tableParts" type="CT_TableParts" minOccurs="0" maxOccurs="1"/>
    <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
  </xsd:sequence>
</xsd:complexType>

I suspect the most likely culprit is PageMargins. DataValidations must come before the PageMargins element. You are adding your DataValidations to the end and by default Excel will write PageMargins on a new file.

Assuming it is the PageMargins, the following code should do the trick.

using (SpreadsheetDocument myDoc = SpreadsheetDocument.Open(filepath, true))
{
    WorkbookPart wbPart = myDoc.WorkbookPart;

    //var sheetData = new DocumentFormat.OpenXml.Spreadsheet.SheetData();
    WorksheetPart worksheetPart = GetWorksheetPartByName(myDoc, "Sheet1");

    DataValidations dataValidations = new DataValidations();
    DataValidation dataValidation = new DataValidation()
    {
        Type = DataValidationValues.List,
        AllowBlank = true,
        SequenceOfReferences = new ListValue<StringValue>() { InnerText = "A1:A1048576" }
    };
    Formula1 formula = new Formula1();
    formula.Text = "\"FirstChoice,SecondChoice,ThirdChoice\"";

    dataValidation.Append(formula);
    dataValidations.Append(dataValidation);

    //insert the dataValidations to the correct place
    worksheetPart.Worksheet.InsertBefore(dataValidations, worksheetPart.Worksheet.Descendants<PageMargins>().FirstOrDefault());
}

If it's not PageMargins then it's likely one of the other elements causing the issue. My answer here gives an approach to this problem which is slightly more involved but more reliable.

Upvotes: 1

Related Questions