none
Adding a document variable using Open XML RRS feed

  • Question

  • Hi

    Im in progress of replacing a WebService which creates MS Office documents, spreadsheets at presentations, from VSTO to Open XML, since Microsoft doesn't support the VSTO approach.

    One of the many steps is to update and add DocumentVariables to Word documents.

    I have figured out how to change existing values, but I can't get the adding of a variable to work.

    Does anybody has some working code (preferably VB, but C# is OK).

    Thanks in advance


    Best Regards Peter Karlström Midrange AB, Sweden

    Tuesday, January 8, 2013 3:15 PM

Answers

  • Hi Peter

    It did work - in C#, but not when I tried your code in VB.NET (finally had time in the VB.NET environment). There I saw what you saw, and the error is logical...

    This works for me in VB.NET. I always felt the problem came from generating new parts and elements (New Settings, for example) but I was thrown by the fact that it worked for me in C#.

    Edit: Now I realize why it was working in C#. From your original problem description I'd made the assumption that the document would already have document variables. And the snag you're hitting only occurs when there are none, to begin with.

        Private Sub btnAddDocVars_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAddDocVars.Click
            'Private Function WDSetVariable(ByVal tDocPath As String, ByVal varName As String, ByVal varValue As String) As Boolean
            Dim tDocPath As String = "C:\Beispiele\TestDocVars.docx"
            Dim varName As String = "TestVar2", varValue As String = "TestVar2Val"
            Me.TextBox1.Text = String.Empty
    
            Using pkg As WordprocessingDocument = WordprocessingDocument.Open(tDocPath, True)
                Dim mainPart As MainDocumentPart = pkg.MainDocumentPart
                Dim settingsPart As DocumentSettingsPart = mainPart.DocumentSettingsPart
                If settingsPart Is Nothing Then
                    settingsPart = mainPart.AddNewPart(Of DocumentSettingsPart)()
                End If
                Dim s As Settings = settingsPart.Settings
                If s Is Nothing Then
                    s = New Settings
                End If
                Dim t As AttachedTemplate = s.Descendants(Of AttachedTemplate)().FirstOrDefault()
                Dim tRef As String = settingsPart.GetExternalRelationship(t.Id).Uri.ToString()
                Me.TextBox1.Text = Me.TextBox1.Text & ("1. Attached template: " & tRef) & Environment.NewLine
    
                Dim docVars As DocumentVariables = s.Descendants(Of DocumentVariables)().FirstOrDefault()
                If docVars Is Nothing Then
                    docVars = New DocumentVariables()
                    s.Append(docVars)
                    'docVars = settingsPart.Settings.Descendants(Of DocumentVariables)().FirstOrDefault()
                End If
                Dim docVar As New DocumentVariable() With {.Name = varName, .Val = varValue}
                docVars.Append(docVar)
    
                t = settingsPart.Settings.Descendants(Of AttachedTemplate)().FirstOrDefault()
                tRef = settingsPart.GetExternalRelationship(t.Id).Uri.ToString()
                Me.TextBox1.Text = Me.TextBox1.Text & ("2. Attached template: " & tRef) & Environment.NewLine
            End Using
    
            'Return True
    
        End Sub


    Cindy Meister, VSTO/Word MVP, my blog


    Sunday, January 13, 2013 9:05 AM
    Moderator

All replies

  • Hi Peter

    I'd already closed down my VM with the VB coding environment, so let's try C#. This works for me. Note that document variables are part of the Settings, not the document, itself.

    private void btnAddDocVar_Click(object sender, EventArgs e)
    {
        string filePath = @"C:\Test\TestDocVars.docx";
        using(WordprocessingDocument pkg = WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document))
        {
            MainDocumentPart mainPart = pkg.AddMainDocumentPart();
            mainPart.Document = new Document(new Body (new Paragraph (new Run (new Text("Text")))));
            Document doc = mainPart.Document;
            DocumentSettingsPart settingsPart = mainPart.AddNewPart<DocumentSettingsPart>();
            settingsPart.Settings = new Settings( new DocumentVariables (
                new DocumentVariable() {Name="Var1", Val="Test1"}));
            
        }
    
    }


    Cindy Meister, VSTO/Word MVP, my blog

    Tuesday, January 8, 2013 4:54 PM
    Moderator
  • Hi Cindy

    Thank you for your reply.

    I have tested a VB-converted sample which adds the value, but seems to mess up the attachedTemplate value for the document.
    Your code created a new document and a new manDocumentPart, but in my scenario I already have a document which is created from a Word template and is updated with coreProperties and customProperties.

    Below is my code for the variable setting:

        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
    
            Dim fName As String = "C:\Temp\OpenXML\SKBTest165744565.docx"
            Dim varName As String = "Testvar"
            Dim varValue As String = "Testvalue"
    
            Using tDoc As WordprocessingDocument = WordprocessingDocument.Open(fName, True)
                Dim mainPart As MainDocumentPart = tDoc.MainDocumentPart
                Dim settingsPart As DocumentSettingsPart = mainPart.DocumentSettingsPart
                settingsPart.Settings = New Settings(New DocumentVariables(New DocumentVariable() With {.Name = varName, .Val = varValue}))
            End Using
        End Sub
    

    Before the routine is executed the attached template is a Word-template other than Normal.
    After the execution the attached template is Normal.

    This is an unexpected change of the documents settings, which I can't have happen.

    Can you find the error?


    Best Regards Peter Karlström Midrange AB, Sweden

    Wednesday, January 9, 2013 4:16 PM
  • Please tell us the version of Word and the Open XML SDK you're using. And if it's 2.0, download, install and reference 2.5, instead and see if that makes a difference.

    Cindy Meister, VSTO/Word MVP, my blog

    Wednesday, January 9, 2013 4:22 PM
    Moderator
  • Version of Word is 2010, 14.0.6129.5000 32-bit

    Version of Open XML SDK is 2.5.5631.0 (DocumentFormat.OpenXml.dll)


    Best Regards Peter Karlström Midrange AB, Sweden

    Wednesday, January 9, 2013 4:28 PM
  • Also, this is the code for creating the document from the template:

        Private Function CreateWordDoc(ByVal templatePath As String, ByVal documentPath As String) As String
    
            Dim oldCulture As Globalization.CultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture
            System.Threading.Thread.CurrentThread.CurrentCulture = New System.Globalization.CultureInfo("en-US")
    
            ' Create a copy of the template file and open the copy
            File.Copy(templatePath, documentPath, True)
    
            File.SetAttributes(documentPath, FileAttributes.Normal)
            Using document As WordprocessingDocument = WordprocessingDocument.Open(documentPath, True)
                ' Change the document type to Document
                document.ChangeDocumentType(DocumentFormat.OpenXml.WordprocessingDocumentType.Document)
    
                ' Get the MainPart of the document
                Dim mainPart As MainDocumentPart = document.MainDocumentPart
    
                ' Get the Document Settings Part
                Dim documentSettingPart1 As DocumentSettingsPart = mainPart.DocumentSettingsPart
    
                ' Create a new attachedTemplate and specify a relationship ID
                Dim attachedTemplate1 As New AttachedTemplate() With {.Id = "relationId1"}
                attachedTemplate1.Id = "relationId1"
    
                ' Append the attached template to the DocumentSettingsPart 
                documentSettingPart1.Settings.Append(attachedTemplate1)
    
                Dim myUri As Uri = New Uri(templatePath, UriKind.Absolute)
    
                documentSettingPart1.AddExternalRelationship("http://schemas.openxmlformats.org/officeDocument/2006/relationships/attachedTemplate", New Uri(myUri.AbsoluteUri), "relationId1")
    
                ' Save the document            
                mainPart.Document.Save()
            End Using
    
            Return documentPath
    
            System.Threading.Thread.CurrentThread.CurrentCulture = oldCulture
    
        End Function
    


    Best Regards Peter Karlström Midrange AB, Sweden

    Wednesday, January 9, 2013 4:31 PM
  • Hi Peter

    The following approach works fine for me (C# again - sorry!) It does not affect the attached template in any way... I note, however, that you're actually adding the AttachedTemplate information, apparently before working with the variables. Perhaps you should perform that and the DocVar stuff at the same time, or at least with the same Part object?

    private void btnAddDocVar_Click(object sender, EventArgs e)
    {
        string filePath = @"C:\Test\DocVarTest.docx";
        using (WordprocessingDocument pkg = WordprocessingDocument.Open(filePath, true))
        {
            MainDocumentPart mainPart = pkg.MainDocumentPart;
            DocumentSettingsPart settingsPart = mainPart.DocumentSettingsPart;
            if (settingsPart == null)
            {
                settingsPart = mainPart.AddNewPart<DocumentSettingsPart>();
            }
            AttachedTemplate t = settingsPart.Settings.Descendants<AttachedTemplate>().FirstOrDefault();
            string tRef = settingsPart.GetExternalRelationship(t.Id).Uri.ToString();
            this.txtMessages.Text = "Attached template: " + tRef;
    
            DocumentVariables docVars = settingsPart.Settings.Descendants<DocumentVariables>().FirstOrDefault();
            if (docVars == null)
            {
                settingsPart.Settings = new Settings( new DocumentVariables());
            }
            DocumentVariable docVar = new DocumentVariable( ){Name="Var2", Val="TestVar2" };
            docVars.Append(docVar);
    
            t = settingsPart.Settings.Descendants<AttachedTemplate>().FirstOrDefault();
            tRef = settingsPart.GetExternalRelationship(t.Id).Uri.ToString();
            this.txtMessages.Text += "Attached template: " + tRef;
        }
    
    }


    Cindy Meister, VSTO/Word MVP, my blog

    Wednesday, January 9, 2013 5:56 PM
    Moderator
  • Hi Cindy

    Thank you for your quick responses.

    I have converterd the code to VB and here is how it looks:

        Private Function WDSetVariable(ByVal tDocPath As String, ByVal varName As String, ByVal varValue As String) As Boolean
    
            Using pkg As WordprocessingDocument = WordprocessingDocument.Open(tDocPath, True)
                Dim mainPart As MainDocumentPart = pkg.MainDocumentPart
                Dim settingsPart As DocumentSettingsPart = mainPart.DocumentSettingsPart
                If settingsPart Is Nothing Then
                    settingsPart = mainPart.AddNewPart(Of DocumentSettingsPart)()
                End If
                Dim t As AttachedTemplate = settingsPart.Settings.Descendants(Of AttachedTemplate)().FirstOrDefault()
                Dim tRef As String = settingsPart.GetExternalRelationship(t.Id).Uri.ToString()
                System.Diagnostics.Debug.Print("Attached template: " + tRef)
    
                Dim docVars As DocumentVariables = settingsPart.Settings.Descendants(Of DocumentVariables)().FirstOrDefault()
                If docVars Is Nothing Then
                    settingsPart.Settings = New Settings(New DocumentVariables())
                    docVars = settingsPart.Settings.Descendants(Of DocumentVariables)().FirstOrDefault()
                End If
                Dim docVar As New DocumentVariable() With {.Name = varName, .Val = varValue}
                docVars.Append(docVar)
    
                t = settingsPart.Settings.Descendants(Of AttachedTemplate)().FirstOrDefault()
                tRef = settingsPart.GetExternalRelationship(t.Id).Uri.ToString()
                System.Diagnostics.Debug.Print("Attached template: " + tRef)
            End Using
    
            Return True
    
        End Function

    Unfortunately I get an error in the second part of getting AttachedTemplate. On the second part t is Nothing.

    Also I had to reinitialize docVars if it was Nothing in the step above (as you can see in the code), but the last t=SettingsPart.... doesn't find a descendant of type AttachedTemplate.

    Does this really work at your end?


    Best Regards Peter Karlström Midrange AB, Sweden

    Friday, January 11, 2013 9:02 AM
  • Hi Peter

    It did work - in C#, but not when I tried your code in VB.NET (finally had time in the VB.NET environment). There I saw what you saw, and the error is logical...

    This works for me in VB.NET. I always felt the problem came from generating new parts and elements (New Settings, for example) but I was thrown by the fact that it worked for me in C#.

    Edit: Now I realize why it was working in C#. From your original problem description I'd made the assumption that the document would already have document variables. And the snag you're hitting only occurs when there are none, to begin with.

        Private Sub btnAddDocVars_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAddDocVars.Click
            'Private Function WDSetVariable(ByVal tDocPath As String, ByVal varName As String, ByVal varValue As String) As Boolean
            Dim tDocPath As String = "C:\Beispiele\TestDocVars.docx"
            Dim varName As String = "TestVar2", varValue As String = "TestVar2Val"
            Me.TextBox1.Text = String.Empty
    
            Using pkg As WordprocessingDocument = WordprocessingDocument.Open(tDocPath, True)
                Dim mainPart As MainDocumentPart = pkg.MainDocumentPart
                Dim settingsPart As DocumentSettingsPart = mainPart.DocumentSettingsPart
                If settingsPart Is Nothing Then
                    settingsPart = mainPart.AddNewPart(Of DocumentSettingsPart)()
                End If
                Dim s As Settings = settingsPart.Settings
                If s Is Nothing Then
                    s = New Settings
                End If
                Dim t As AttachedTemplate = s.Descendants(Of AttachedTemplate)().FirstOrDefault()
                Dim tRef As String = settingsPart.GetExternalRelationship(t.Id).Uri.ToString()
                Me.TextBox1.Text = Me.TextBox1.Text & ("1. Attached template: " & tRef) & Environment.NewLine
    
                Dim docVars As DocumentVariables = s.Descendants(Of DocumentVariables)().FirstOrDefault()
                If docVars Is Nothing Then
                    docVars = New DocumentVariables()
                    s.Append(docVars)
                    'docVars = settingsPart.Settings.Descendants(Of DocumentVariables)().FirstOrDefault()
                End If
                Dim docVar As New DocumentVariable() With {.Name = varName, .Val = varValue}
                docVars.Append(docVar)
    
                t = settingsPart.Settings.Descendants(Of AttachedTemplate)().FirstOrDefault()
                tRef = settingsPart.GetExternalRelationship(t.Id).Uri.ToString()
                Me.TextBox1.Text = Me.TextBox1.Text & ("2. Attached template: " & tRef) & Environment.NewLine
            End Using
    
            'Return True
    
        End Sub


    Cindy Meister, VSTO/Word MVP, my blog


    Sunday, January 13, 2013 9:05 AM
    Moderator
  • Hi Cindy

    This looks better and works as expected.

    Thanks a lot for your support.


    Best Regards Peter Karlström Midrange AB, Sweden

    Sunday, January 13, 2013 3:26 PM