2

I have to fill an excel like this

enter image description here

Where I have merged cells on each row. I'm trying to use loadfromcollection, but it ignores the merged cells and it fills each single row.

Please did you know any quick way to do it?


edit: I think that one way is to implement a custom loadfromcollection where the steps are:

  1. Iterate collection
  2. For each object, read properties/fields using reflection
  3. Iterate cell on each row
  4. check if range address is in worksheet.mergecells
  5. fill it with property/field value

But in case of complex worksheet (with some loadfromcollection), I think that this approach could be heavy with a big cost.


edit 2: Add an example: Code like this

public class MyObj
{
  public string first {get; set;}
  public string second {get; set;}
}
...
List<MyObj> myList = new List<MyObj>() {
   new MyObj() {first = "first1", second = "second1"},
   new MyObj() {first = "first2", second = "second2"} };
...
ws.Cells["A1"].LoadFromCollection(myList);

In a normal worksheet, loadfromcollection has the following output: enter image description here

If I merge column A and B after filled, I get this:

enter image description here

In case that my worksheet template is

enter image description here

and then I try to fill it using loadfromcollection I get this result

enter image description here

loadfromcollection ignores merged cell, and fill column A and B, but my expectation is

enter image description here

Saledan
  • 85
  • 9
  • Are the cells already merged when you try to fill it, or do you want the filling to both merge 2 cells and then fill it? – JPReumerman Aug 09 '21 at 16:21
  • In this case the cells are already merged. But anyway, if I'm in right, if you fill cell A1 and cell B1 , then merge A1:B1, usually you loose information on B1. – Saledan Aug 09 '21 at 18:23
  • Could you give an example of how you would like it to turn out and of what the actual result is currently. Then I'll take a look. – JPReumerman Aug 09 '21 at 19:27
  • thanks a lot, I just updated original question! – Saledan Aug 10 '21 at 06:47

1 Answers1

1

The problem is that this is an ´intended´ behavior in excel. When you have values in Columns A and B and you merge them, only the value in column A will remain. LoadFromCollection does not have an overload for this kind of scenario.

I can think of several ´workarounds´ which could work, depending on how general you want the code to be.

  1. Your solution of iterating through object properties with reflection would work. If the ´merging´ is always two columns, I´d recommend using a column counter and add 2 to it each property iteration.

So something like this (where of course you already defined the excel package and worksheet etc and the start row and column):

var type = testObject.GetType();
var properties = type.GetProperties();
            
foreach (var property in properties)
{
       cell = worksheet.Cells[row, column];
       cell.Value = property.GetValue(testObject);

       column += 2;
}
  1. Alternatively, you could decide to remove the merging from the template and move it to your code instead. This may provide a little bit more consistency and flexibility as well.

Code above would then look more like:

var type = testObject.GetType();
var properties = type.GetProperties();
            
foreach (var property in properties)
{
       cellRange = worksheet.Cells[row, column, row, column + 1];

       cellRange.Merge = true;
       cellRange.Value = property.GetValue(testObject);
       
       column += 2;
}

You could even go a bit 'crazy' and make the range variable based on the property. You make a switch statement with all known properties which should be 2 merged cells, those which should be 3 merged cells etc. Then make the default 1 cell/column. This way you gain total control over the output and it's much easier to adjust in the future.

Edit

My case is a little bit more complex, not all my mergedcells are composed by 2 cells

Some code to illustrate my above statement with some code to help address this. Used the old switch notation as you said it was an 'old project' and I assume you aren't using C# 8 or higher yet.

var type = testObject.GetType();
var properties = type.GetProperties();
int columnMergeSize;
            
foreach (var property in properties)
{
       columnMergeSize = GetPropertyColumnMergeSize(property.Name);

       cellRange = worksheet.Cells[row, column, row, column + columnMergeSize];

       cellRange.Merge = true;
       cellRange.Value = property.GetValue(testObject);
       
       column += 2;
}

private int GetPropertyColumnMergeSize(string propertyName)
{
    switch (propertyName)
    {
        case "Property1":
        case "Property2":
        case "Property3":
        case "Property4":
            return 2;
        case "Property5":
        case "Property6":
            return 3;
        default:
            return 1;
    }
}

This will be less heavy than having to read out the properties of the cell each time to see if it's 'merged' or not. I've also found that if you remove nearly all formatting from an excel template it will load much faster speeding up your programme.

You can either decide to put this in a service and just inject the method into wherever it's needed with DI, or you can make it a static method and use it.

JPReumerman
  • 105
  • 1
  • 9
  • thanks a lot. My case is a little bit more complex, not all my mergedcells are composed by 2 cells. But anyway your code could be useful. I'll do static method because is an old project. Thanks a lot again! – Saledan Aug 11 '21 at 14:02
  • 1
    I've added some additional code to illustrate my comment on making the 'merge size' flexible. Hope it helps, good luck ;) If you do have C# 8 or higher on this project, I'd suggest to look into the newer notation as that'll be much cleaner for this case. Using something like: var x when listOfValues.Contains(x) in the switch statement. – JPReumerman Aug 11 '21 at 15:24