Search code examples
c#openxmlopenxml-sdk

DrawingsPart not being added to the WorksheetPart. Specified argument was out of the range of valid values


Using the Open Xml SDK, I added a DrawingsPart to a WorksheetPart and then later I tried referencing the DrawingsPart from within the WorksheetPart but I receive an ArgumentOutOfRangeException.

Here is the relevant snippet of code:

// Add a new drawings part to the worksheet
var drawingsPart = worksheetPart.AddNewPart<DrawingsPart>();

// make a drawing DOM
var drawingRootElement = new WorksheetDrawing();

// add to the drawing DOM
...

// and then...
// associate the drawing DOM to the drawings part
drawingsPart.WorksheetDrawing = drawingRootElement;

// save the drawing DOM back to the drawings part
drawingsPart.WorksheetDrawing.Save();

// and finally...
// here is where it throws the ArgumentOutOfRangeException
// whether I supply the drawingsPart as the argument
// or the value worksheet.DrawingsPart
// it reports the same exception
// I looked up the source of OpenXmlPartContainer.GetIdOfPart
// and it looks like the DrawingsPart is not yet added to the
// PartDictionary of the WorksheetPart. I wonder why?
var relationshipIdOfDrawingsPart = drawingsPart
                            .GetIdOfPart(worksheetPart.DrawingsPart /* drawingsPart */);


// Create a new drawing element and add it to the Worksheet DOM
var drawingElement = new DocumentFormat.OpenXml.Spreadsheet.Drawing { Id = relationshipIdOfDrawingsPart };
worksheetPart.Worksheet.Append(drawingElement);

Exception details:

System.ArgumentOutOfRangeException occurred HResult=0x80131502
Message=Specified argument was out of the range of valid values.
Source=DocumentFormat.OpenXml StackTrace: at DocumentFormat.OpenXml.Packaging.OpenXmlPartContainer.GetIdOfPart(OpenXmlPart part) at ... my code

I looked up the source of OpenXmlPartContainer.GetIdOfPart (reproduced below):

// DocumentFormat.OpenXml.Packaging.OpenXmlPartContainer
/// <summary>
/// Gets the relationship ID of the part.
/// </summary>
/// <param name="part">The part.</param>
/// <returns>The relationship ID of the part.</returns>
/// <exception cref="T:System.ArgumentNullException">Thrown when "part" is null reference.</exception>
/// <exception cref="T:System.ArgumentOutOfRangeException">Thrown when the part does not exist.</exception>
public string GetIdOfPart(OpenXmlPart part)
{
    this.ThrowIfObjectDisposed();
    if (part == null)
    {
        throw new ArgumentNullException("part");
    }
    if (this.PartDictionary.ContainsValue(part))
    {
        foreach (KeyValuePair<string, OpenXmlPart> current in this.PartDictionary)
        {
            if (part == current.Value)
            {
                return current.Key;
            }
        }
    }
    throw new ArgumentOutOfRangeException("part");
}

It looks like the DrawingsPart is not being added to the PartDictionary of the WorksheetPart. I wonder why?


Solution

  • Given that worksheetPart.DrawingsPart and drawingsPart are the same object, this code does not make sense:

    // and finally...
    // here is where it throws the ArgumentOutOfRangeException
    // whether I supply the drawingsPart as the argument
    // or the value worksheet.DrawingsPart
    // it reports the same exception
    // I looked up the source of OpenXmlPartContainer.GetIdOfPart
    // and it looks like the DrawingsPart is not yet added to the
    // PartDictionary of the WorksheetPart. I wonder why?
    var relationshipIdOfDrawingsPart = drawingsPart
                                .GetIdOfPart(worksheetPart.DrawingsPart /* drawingsPart */);
    

    since that would only work if the object's PartDictionary contained a reference to itself.

    As such, you need to instead call GetIdOfPart on the parent object, not drawingsPart itself.