how to set values to a two-dimensional Excel range?

Elad picture Elad · Jun 12, 2013 · Viewed 10.6k times · Source

I need to build an excel sheet from a list of test-cases in a specific format in order to upload it it to the server. I've trubles to populate the two dimensional range of "expected" and "actual" in the file. I use the same methods in order to populate the headers, which is a one-dimensional array, and the steps (which is two-dims).

The flow is:

  1. Defunding the TestCase range (some headers + steps). Let's say: A1 to E14 for the 1st iteration.
  2. Depunding a sub (local) range within the testCase range for the headers (e.g: A1 to C1).
  3. Depunding another sub (local) range within the testCase range for the headers (in my case: D1 to E14).
  4. Populate the two sub-ranges with a test-case values (headers and steps).
  5. Repeat by defunding the next spreadsheet range (A14 to E28 in my case) with same local ranges (steps 2-3), and populate them, and so on...

The source value is a Dictionary which represents the test-case's steps (key = expected and value = actual).

Here is the code I use:

public class TestCase
{
    public Dictionary<string, string> steps;
}

Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
Workbooks workBooks = excelApp.Workbooks;
Workbook workbook = workBooks.Add(XlWBATemplate.xlWBATWorksheet);
Worksheet worksheet = (Worksheet)workbook.Worksheets[1];
excelApp.Visible = true;

foreach (TestCase testCase in TestCaseList.Items.GetList())
{
    Range worksheetRange = GetRangeForTestCase(worksheet);
    //The step above is equivalent to:
    //    Range worksheetRange = worksheet.get_Range("A1", "E14");
    //for the first foreach iteration.

    Range stepsRange = worksheetRange.get_Range("D1", "E14");
    //This is a local range within the worksheetRange, not the worksheet,
    //so it is always have to be between D1 to E14 for a 14th steps test case.
    //Anyway, it should work at least for the 1st iteration.

    //for test evaluation only. All cells between D1 to E14 are filled with "ccc"
    test.Value = "ccc";

    //This list of pairs which are converted to Array[,] is about to be converted to a two dimensional array
    list = new List<object>();
    foreach (KeyValuePair<string, string> item in testCase.Steps)
    {
        //Here I build the inside Array[,]
        object[] tempArgs = {item.Key, item.Value};
        list.Add(tempArgs);
    }
    object[] args = { Type.Missing, list.ToArray() };

    test.GetType().InvokeMember("Value", System.Reflection.BindingFlags.SetProperty, null, test, args);

    //Now, all the "ccc" within the Excel worksheet are disapeared, so the Range is correct, but the value of args[] are not set!!
}

The actual results of running this code is that the range is defined (probably correctly) but its values are set to null, although - I can see the correct values in the args array in run time. I've also tried to set a wider range and populate it with range.Value = "Pake value" and saw that, after running my peace of code, the correct range of steps become blank! So, the range is correct, the array is filled with my values, the InvokeMember method is correctly invoked :) But, all values are set to null..

Help...

Answer

Elad picture Elad · Jun 17, 2013

One cell or 1dim array can be set via one of the following:

Range range = SetRange();//Let's say range is set between A1 to D1
object[] args = {1, 2, 3, 4 }; 

//Directly
range.Value = args;

//By Reflection
range.GetType().InvokeMember("Value", System.Reflection.BindingFlags.SetProperty, null, range, args);

A 2dim array cannot be directly set, so one has to use the reflection flow to set a matrix of values. This matrix has to be built before the set, like this:

Range range = SetRange();//Let's say range is set between A1 to C5
int rows = 5;
int columns = 3;
object[,] data = new object[rows, columns];
for (int i = 0; i < rows; i++)
{
    for (int j = 0; j < columns; j++)
    {
        //Here I build the inside Array[,]
        string uniqueValue = (i + j).ToString();
        data[i, j] = "Insert your string value here, e.g: " + uniqueValue;
    }
}
object[] args = { data };
range.GetType().InvokeMember("Value", System.Reflection.BindingFlags.SetProperty, null, range, args);