Need to replace CustomXML with new XML from string

I want to use a .docx format document as a template, using form controls and CustomXML. What I want to do to create the generated document is to simply replace the CustomXML "item1.xml" with a new bag of XML containing data that I've been passed.

It seems as if I should be able to do this in about 4 statements, but I must not be thinking about the mechanics right since I'm not succeeding.

I'd like to use the WordprocessingDocument class, find the CustomXML part, and do a method like LoadXML to take my in-memory XML and stuff it into "item1.xml." Truncating current contents of "item1.xml" and writing into it would be ideal, but if I have to delete, add, and load that's OK too. All I see available as a method is FeedData, and I don't have a stream, I have a string.

I know that I should be able to do this programmatically; I've got a document with CustomXML mapped to controls, displaying correctly. I can create a different CustomXML file, manually remove the old Custom XML, put in the new one, and voila, Word displays the new package just fine.

Would somebody be kind enough to show me the key code? I'm sure that other people are doing the same thing as what I'm trying to do.

TIA

Addendum - Got this working using Package and PackagePart. It was a little more obscure than what I wanted to do but not too much code.

[1456 byte] By [D.Jones] at [2008-2-21]
# 1

Can you post your solution, I am investigating the same problem.

Thank you in advance

Bluerouses at 2007-10-2 > top of Msdn Tech,Office Live Development,Microsoft SDK for Open XML Formats...
# 2

If you know that your CustomXML file has only one part, etc., it can be as simple as

customXml.LoadXml(xmlToApply);

uri = new Uri("/customXML/item1.xml", UriKind.Relative);

PackagePart partOut = packageOut.GetPart(uri);

using (Stream stmWritePart = partOut.GetStream(System.IO.FileMode.Create, System.IO.FileAccess.Write))

{

customXml.Save(stmWritePart);

}

packageOut.Close();

Does this help?

D.Jones at 2007-10-2 > top of Msdn Tech,Office Live Development,Microsoft SDK for Open XML Formats...
# 3

Hi D.

I'm very busy now but I'm sure the solution is not hard.

I think , nevertheless , I can help: I changed (some time ago) an image file from the docx package recoding one of the code snippets for Visual Studio: one of Powerpoint that contains the hard side of the issue.

Try it.

MauricioG at 2007-10-2 > top of Msdn Tech,Office Live Development,Microsoft SDK for Open XML Formats...
# 4

You're saying that you used the WordprocessingDocument class successfully to replace the CustomXML file, and that I can find that code in a Powerpoint presentation somewhere?

Thanks

D.Jones at 2007-10-2 > top of Msdn Tech,Office Live Development,Microsoft SDK for Open XML Formats...
# 5

I was looking for my code and I need more time to find it.

What I'm trying to said is that into the Code Snippets for Open XML for Visual Studio (http://www.microsoft.com/downloads/details.aspx?familyid=8d46c01f-e3f6-4069-869d-90b8b096b556&displaylang=en ) there is one snippet with the code necessary to change an Image for another . The code does it byte by byte.

Below is the snippet, look for [' Now copy the bytes into the new part...] (tell me please if it is useful to you) :


Public Sub PPTReplaceImageOnSlide(ByVal fileName As String, ByVal slideTitle As String, ByVal imagePath As String)
' Given a slide deck name and a slide title within the deck,
' replace the first image on the slide with the supplied image.
' If the slide doesn't have an image on it, do nothing.

Const documentRelationshipType As String = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
Const slideRelationshipType As String = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide"
Const imageRelationshipType As String = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
Const imageContentType As String = "image/jpeg"

Const presentationmlNamespace As String = "http://schemas.openxmlformats.org/presentationml/2006/main"
Const drawingmlNamespace As String = "http://schemas.openxmlformats.org/drawingml/2006/main"
Const relationshipNamespace As String = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"

Dim documentPart As PackagePart = Nothing
Using pptPackage As Package = Package.Open(fileName, FileMode.Open, FileAccess.ReadWrite)
' Get the main document part (presentation.xml).
For Each documentRelationship As PackageRelationship In pptPackage.GetRelationshipsByType(documentRelationshipType)
Dim documentUri As Uri = PackUriHelper.ResolvePartUri(New Uri("/", UriKind.Relative), documentRelationship.TargetUri)
documentPart = pptPackage.GetPart(documentUri)
' There is only one document.
Exit For
Next

' Manage namespaces to perform Xml XPath queries.
Dim nt As New NameTable()
Dim nsManager As New XmlNamespaceManager(nt)
nsManager.AddNamespace("p", presentationmlNamespace)
nsManager.AddNamespace("a", drawingmlNamespace)
nsManager.AddNamespace("r", relationshipNamespace)

Dim slidePart As PackagePart = Nothing
Dim slideUri As Uri = Nothing

' Select each slide document part (slides/slideX.xml)
' via relationship with document part.
For Each slidePartRelation As PackageRelationship In documentPart.GetRelationshipsByType(slideRelationshipType)
slideUri = PackUriHelper.ResolvePartUri(documentPart.Uri, slidePartRelation.TargetUri)
slidePart = pptPackage.GetPart(slideUri)

' Get the slide part from the package.
Dim slideDoc As XmlDocument = New XmlDocument(nt)
slideDoc.Load(slidePart.GetStream())

' Locate the slide title using XPath.
Dim titleNode As XmlNode = slideDoc.SelectSingleNode("//pTongue Tiedp//pStick out tongueh[@type='title' or @type='ctrTitle']", nsManager)
If titleNode IsNot Nothing Then
Dim titleText As String = titleNode.ParentNode.ParentNode.ParentNode.InnerText

' Perform a case-insensitive comparison.
If String.Compare(titleText, slideTitle, True) = 0 Then
' Found a match. Modify this slide.
Dim imagePart As PackagePart = Nothing

' Look through the slide's relationships to see if there's an image in there:
For Each imagePartRelation As PackageRelationship In slidePart.GetRelationshipsByType(imageRelationshipType)
Dim imageUri As Uri = PackUriHelper.ResolvePartUri(slidePart.Uri, imagePartRelation.TargetUri)
imagePart = pptPackage.GetPart(imageUri)
' We only care about the first image.
' Can't actually delete the image -- someone else might
' be using it. Simply create a new part and reset the relationship
' to point to the new part. PPT will take care of redundant relationships.
If imagePart IsNot Nothing Then
' You've got an image!
Dim oldRelID As String = imagePartRelation.Id

' Create a new part for the new image
' (Be warned: If the package already contains
' a part with this name (unlikely), you wouldn't be able
' to create the new part):
Dim imageFile As String = Path.GetFileName(imagePath)
Dim newImageUri As Uri = New Uri("/ppt/media/" & imageFile, UriKind.Relative)
Dim newImagePart As PackagePart = _
pptPackage.CreatePart(newImageUri, imageContentType)

' Now copy the bytes into the new part...
Using outputStream As Stream = newImagePart.GetStream(FileMode.Create, FileAccess.Write)
Using inputStream As New FileStream(imagePath, FileMode.Open, FileAccess.Read)
' This will fail if the image is huge (more bytes than
' an int can contain)
Dim len As Integer = Convert.ToInt32(inputStream.Length)
Dim bytes(0 To len - 1) As Byte
Dim bytesRead As Integer = inputStream.Read(bytes, 0, len)
If bytesRead = len Then
outputStream.Write(bytes, 0, len)
End If
End Using
End Using

' Create a new relationship to the new part:
Dim newRelation As PackageRelationship = _
slidePart.CreateRelationship(newImageUri, TargetMode.Internal, imageRelationshipType)
Dim newRelId As String = newRelation.Id

' Update the relationship in the slide XML:
Dim searchString As String = String.Format("//pStick out tongueic//a:blip[@r:embed='{0}']", oldRelID)
Dim relNode As XmlNode = slideDoc.SelectSingleNode(searchString, nsManager)
If relNode IsNot Nothing Then
relNode.Attributes("r:embed").Value = newRelId
End If

' Delete the old relationship
' PowerPoint will clean up the orphaned image part on the next save.
slidePart.DeleteRelationship(oldRelID)

' Only update the first image you run across:
Exit For
End If
Next

' Reset the stream, and save the slide XML back to its part.
slideDoc.Save(slidePart.GetStream(FileMode.Create, FileAccess.Write))

' Only modify the first slide that matches the specified title.
Exit For
End If
End If
Next
End Using
End Sub

MauricioG at 2007-10-2 > top of Msdn Tech,Office Live Development,Microsoft SDK for Open XML Formats...