Click here to Skip to main content
16,022,297 members
Please Sign up or sign in to vote.
1.00/5 (2 votes)
See more:
Open xml with data validation for Named Range

What I have tried:

public static void AddDropdowns(WorkbookPart workbookPart,DataGridViewColumnCollection columns, string sheetName)
        {
            // Get the target worksheet
            var sheet = workbookPart.Workbook.Descendants<Sheet>().FirstOrDefault(s => s.Name == sheetName);
            if (sheet == null)
            {
                throw new ArgumentException($"Sheet with name '{sheetName}' not found.");
            }

            var worksheetPart = (WorksheetPart)workbookPart.GetPartById(sheet.Id);

            // Create validation sheet only once if it doesn't exist
            WorksheetPart validationWorksheetPart = CreateValidationSheetIfNotExists(workbookPart, sheetName);


           
            for (var i = 0; i < columns.Count; i++)
            {
                var currentColumn = columns[i];
                if (currentColumn.GetType() != typeof(DataGridViewComboBoxColumn)) { continue; }

                var comboColumn = (DataGridViewComboBoxColumn)currentColumn;
                var dataColNum = i + 1 + additionalPrefixColumns; // Adjust this based on your needs
                var dataColumn = GetExcelColumnName(dataColNum);
                var dataStart = dataColumn + firstDataRow; // Assuming firstDataRow is 3
                var rowCount = worksheetPart.Worksheet.GetFirstChild<SheetData>().Elements<Row>().Count();
                var dataEnd = dataColumn + rowCount;
                var validationName = "validation_" + currentColumn.Name;

                // Populate validation options
                var optionsDt = new VC_ExportToExcelDropdownOptions(worksheetPart, dataStart, dataEnd,
                    validationWorksheetPart, dataColumn, comboColumn.Items, comboColumn.ValueMember, validationName);

                object[,] dataArray = ExtractPropertyPerItem(optionsDt.Options, optionsDt.OptionKey);

                // Define the named range only once
                DefineNamedRange(workbookPart, worksheetPart, validationName, sheetName, dataStart, dataEnd);

                // Add the dropdown options using Open XML
                AddDataValidation(workbookPart, worksheetPart, dataStart, dataEnd, dataArray, validationName, validationWorksheetPart);
            }
        }

        // This method ensures that the validation sheet is created once and reused.
        private static WorksheetPart CreateValidationSheetIfNotExists(WorkbookPart workbookPart, string validationName)
        {
            // Create or get the hidden validation sheet to store dropdown values
            const string validationSheetName = "ValidationSheet";

            var validationSheet = workbookPart.Workbook.Descendants<Sheet>()
                .FirstOrDefault(s => s.Name == validationSheetName);

            WorksheetPart hiddenSheetPart;
            if (validationSheet == null)
            {
                // Create new hidden sheet for validation
                hiddenSheetPart = workbookPart.AddNewPart<WorksheetPart>();
                hiddenSheetPart.Worksheet = new Worksheet(new SheetData());
                Sheets sheets = workbookPart.Workbook.GetFirstChild<Sheets>();
                uint sheetId = sheets.Elements<Sheet>().Select(s => s.SheetId.Value).Max() + 1;

                sheets.Append(new Sheet
                {
                    Id = workbookPart.GetIdOfPart(hiddenSheetPart),
                    SheetId = sheetId,
                    Name = validationSheetName,
                    State = SheetStateValues.Hidden // Hide the sheet
                });

                workbookPart.Workbook.Save();
                Console.WriteLine($"Created new validation sheet: {validationSheetName}");
            }
            else
            {
                hiddenSheetPart = (WorksheetPart)workbookPart.GetPartById(validationSheet.Id);
                Console.WriteLine($"Using existing validation sheet: {validationSheetName}");
            }
            return hiddenSheetPart;
        }

        // AddDataValidation is responsible for adding the dropdown data validation
        private static void AddDataValidation(
            WorkbookPart workbookPart,
            WorksheetPart worksheetPart,
            string startCell,
            string endCell,
            object[,] options,
            string validationName,
            WorksheetPart hiddenSheetPart)
        {
            var worksheet = worksheetPart.Worksheet;
            var dataValidations = worksheet.Elements<DataValidations>().FirstOrDefault();

            // Create DataValidations if not exists
            if (dataValidations == null)
            {
                dataValidations = new DataValidations();
                worksheet.Append(dataValidations);
            }

            // Step 2: Add options to the hidden validation sheet
            //var hiddenSheetPart = CreateValidationSheetIfNotExists(workbookPart, validationName);  // Ensure validation sheet exists
            var sheetData = hiddenSheetPart.Worksheet.GetFirstChild<SheetData>();
            sheetData.RemoveAllChildren(); // Clean existing rows
            
            var optionList = new List<string>();
            for (int i = 0; i < options.GetLength(0); i++)
            {
                if (options[i, 0] != null)
                {
                    optionList.Add(options[i, 0].ToString());
                }
            }

            // Define the list of dropdown values
            //var dropDownOptions = options.Select(o => o.Value).ToList();
            //string formula = "\"" + string.Join(",", optionList) + "\"";


            //// Populate the validation sheet
            for (int i = 0; i < options.GetLength(0); i++)
            {
                if (options[i, 0] != null)
                {
                    var row = new Row();
                    row.Append(new Cell { CellValue = new CellValue(options[i, 0].ToString()), DataType = CellValues.String });
                    sheetData.Append(row);
                }
            }
            //hiddenSheetPart.Worksheet.Save();
            var range = $"{startCell}:{endCell}";
            // Create data validation referencing the named range
            var dataValidation = new DataValidation
            {
                Type = DataValidationValues.List,
                AllowBlank = true,
                ShowInputMessage = true,
                ShowErrorMessage = true,
                SequenceOfReferences = new ListValue<StringValue> { InnerText = $"{startCell}:{endCell}" },
                Formula1 = new Formula1($"={validationName}"), // Reference the named range
                //Formula1 = new Formula1(formula),
                ErrorTitle = "Invalid Option",
                Error = "Please select a value from the dropdown"
            };

            dataValidations.Append(dataValidation);
            dataValidations.Count = (uint)dataValidations.Count();
            //worksheet.Append(dataValidations);

            // Save the worksheet after making changes
            worksheet.Save();
        }


        private static void DefineNamedRange(
            WorkbookPart workbookPart,
            WorksheetPart worksheetPart,
            string rangeName,
            string sheetName,
            string startCell,
            string endCell)
        {
            var definedNames = workbookPart.Workbook.GetFirstChild<DefinedNames>();
            if (definedNames == null)
            {
                definedNames = new DefinedNames();
                workbookPart.Workbook.Append(definedNames);
            }

            var definedName = new DefinedName
            {
                Name = rangeName,
                Text = $"{sheetName}!{startCell}:{endCell}"
            };
            definedNames.Append(definedName);
            workbookPart.Workbook.Save();
        }

        private static bool NamedRangeExists(WorkbookPart workbookPart, string rangeName)
        {
            return workbookPart.Workbook.GetFirstChild<DefinedNames>()
                   ?.Elements<DefinedName>()
                   .Any(dn => dn.Name == rangeName) ?? false;
        }



        public static object[,] ExtractPropertyPerItem(IList optionList, string propertyName)
        {
            int validationEndCellNum = optionList.Count;
            var dataArray = new object[validationEndCellNum, 1];
            const int column = 0;
            for (int i = 0; i < validationEndCellNum; i++)
            {
                object currentObj = optionList[i];
                Type type = currentObj.GetType();
                if (type == typeof(string))
                {
                    dataArray[i, column] = currentObj;
                    continue;
                }
                PropertyInfo propertyInfo = type.GetProperty(propertyName);
                if (propertyInfo.PropertyType == typeof(String) || propertyInfo.PropertyType == typeof(int))
                {
                    dataArray[i, column] = propertyInfo.GetValue(currentObj, null);
                }
            }
            return dataArray;
        }
Posted
Comments
Member 12077944 21-Sep-24 11:44am    
I have tried with this code, but its not giving me the dropdown values instead, its breaking the excel while opening and sometimes its removing the while data validation.

Can someone lookinto this code find whats the problem

1 solution

We don't have your data, and there is a good chance that it is relevant to what is happening - we also have no idea what "its breaking the excel while opening" or "sometimes its removing the while data validation" actually mean.

So, it's going to be up to you.
Fortunately, you have a tool available to you which will help you find out what is going on: the debugger. If you don't know how to use it then a quick Google for "Visual Studio debugger" should give you the info you need.

Put a breakpoint on the first line in the function, and run your code through the debugger. Then look at your code, and at your data and work out what should happen manually. Then single step each line checking that what you expected to happen is exactly what did. When it isn't, that's when you have a problem, and you can back-track (or run it again and look more closely) to find out why.

Sorry, but we can't do that for you - time for you to learn a new (and very, very useful) skill: debugging!
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900