Open XML オブジェクト モデルを使用して Word 2007 ファイルを操作する (パート 3/3)

概要 : この記事は、Microsoft Office Word 2007 ファイルへのアクセスと操作に使用可能な Open XML オブジェクト モデル コードについて説明している 3 つの記事のシリーズの 3 番目にあたります。(16 印刷ページ)

Frank Rice、Microsoft Corporation

2007 年 9 月

適用対象 : Microsoft Office Word 2007

目次

  • 概要

  • カスタム ドキュメント プロパティを設定する

  • ドキュメントの印刷方向を設定する

  • まとめ

概要

2007 Microsoft Office system では、Office Open XML 形式と呼ばれる、XML に基づく新しいファイル形式が導入されています。Microsoft Office Word 2007、Microsoft Office Excel 2007、および Microsoft Office PowerPoint 2007 では、このファイル形式が既定のファイル形式として使用されます。Microsoft では、Microsoft .NET Framework 3.0 テクノロジの一部として、System.IO.Packaging 名前空間でこれらのファイルにアクセスするためのライブラリを「Microsoft SDK for Open XML Formats テクノロジ プレビュー」に用意しています。Open XML オブジェクト モデルは System.IO.Packaging API を基盤として作成されており、Open XML ドキュメントを操作するための厳密に型指定されたパーツ クラスを備えています。ここでは、サンプル コードにより、Open XML オブジェクト モデルを使用して Word 2007 ファイルにアクセスおよび操作する方法を説明します。

カスタム ドキュメント プロパティを設定する

以下のコードでは、ドキュメントのカスタム プロパティの値を設定します。ドキュメントは、custom.xml パーツにあるカスタム プロパティを含む場合もあり、含まない場合もあります。このため、プロシージャは以下の処理を行います。

  • custom.xml パーツがドキュメントに存在しない場合は、それを追加します。

  • custom.xml パーツがドキュメントに存在し、プロパティが存在ない場合は、プロパティを追加します。

  • プロパティが存在し、同じ種類のものである場合は、値を置き換えます。

  • プロパティが存在し、別の種類のものである場合は、既存のプロパティを更新します。

完了したら、プロシージャは操作が正常に完了したかどうかを示す Boolean 値を返します。

Public Enum PropertyTypes
   YesNo
   Text
   DateTime
   NumberInteger
   NumberDouble
End Enum

Public Function WDSetCustomProperty(ByVal docName As String, ByVal propertyName As String, ByVal propertyValue As Object, ByVal 
propertyType As PropertyTypes) As Boolean
   ' Given a document name, a property name/value, and the property
   ' type, add a custom property to a document. Return true if the
   ' property is added/updated, or False if the property cannot be updated.
   Const customPropertiesSchema As String = "http://schemas.liquid-technologies.com/OfficeOpenXML/2006/default.html"
   Const customVTypesSchema As String = "http://schemas.liquid-technologies.com/OfficeOpenXML/2006/default.html"
   Dim retVal As Boolean = False
   Dim propertyTypeName As String = "vt:lpwstr"
   Dim propertyValueString As String = Nothing
   ' Calculate the correct type.
   Select Case (propertyType)
      Case PropertyTypes.DateTime
         propertyTypeName = "vt:filetime"
         If (TypeOf propertyValue Is String) Then
            propertyValueString = String.Format("{0:s}Z", Convert.ToDateTime(propertyValue))
         End If
      Case PropertyTypes.NumberInteger
         propertyTypeName = "vt:i4"
         If (TypeOf propertyValue Is Int32) Then
            propertyValueString = Convert.ToInt32(propertyValue).ToString
         End If
      Case PropertyTypes.NumberDouble
         propertyTypeName = "vt:r8"
         If (TypeOf propertyValue Is Double) Then
            propertyValueString = Convert.ToDouble(propertyValue).ToString
         End If
      Case PropertyTypes.Text
         propertyTypeName = "vt:lpwstr"
         propertyValueString = Convert.ToString(propertyValue)
      Case PropertyTypes.YesNo
         propertyTypeName = "vt:bool"
         If (TypeOf propertyValue Is Boolean) Then
            ' Must be lowercase!
            propertyValueString = Convert.ToBoolean(propertyValue).ToString.ToLower
         End If
   End Select
   If (propertyValueString = Nothing) Then
      ' If the code cannot convert the 
      ' property to a valid value, throw an exception.
      Throw New InvalidDataException("Invalid parameter value.")
   End If
   Dim wdPackage As WordprocessingDocument = WordprocessingDocument.Open(docName, True)
   ' Work with the custom properties part.
   Dim customPropsPart As CustomFilePropertiesPart = wdPackage.CustomFilePropertiesPart
   ' Manage namespaces to perform XML XPath queries.
   Dim nt As NameTable = New NameTable
   Dim nsManager As XmlNamespaceManager = New XmlNamespaceManager(nt)
   nsManager.AddNamespace("d", customPropertiesSchema)
   nsManager.AddNamespace("vt", customVTypesSchema)
   Dim customPropsUri As Uri = New Uri("/docProps/custom.xml", UriKind.Relative)
   Dim customPropsDoc As XmlDocument = Nothing
   Dim rootNode As XmlNode = Nothing
   ' There may not be a custom properties part.
   If (customPropsPart Is Nothing) Then
      customPropsDoc = New XmlDocument(nt)
      ' The part does not exist. Create it now.
      customPropsPart = wdPackage.AddCustomFilePropertiesPart
      ' Set up the rudimentary custom part.
      rootNode = customPropsDoc.CreateElement("Properties", customPropertiesSchema)
   rootNode.Attributes.Append(customPropsDoc.CreateAttribute("xmlns:vt"))
      rootNode.Attributes("xmlns:vt").Value = customVTypesSchema
      customPropsDoc.AppendChild(rootNode)
   Else
      ' Load the contents of the custom properties part into an XML document.
      customPropsDoc = New XmlDocument(nt)
      customPropsDoc.Load(customPropsPart.GetStream)
      rootNode = customPropsDoc.DocumentElement
   End If
   ' Now that you have a reference to an XmlDocument object that 
   ' corresponds to the custom properties part, 
   ' check to see if the required property is already there.
   Dim searchString As String = String.Format("d:Properties/d:property[@name='{0}']", propertyName)
   Dim node As XmlNode = customPropsDoc.SelectSingleNode(searchString, nsManager)

   Dim valueNode As XmlNode = Nothing
   If (Not (node) Is Nothing) Then
      ' You found the node. Now check its type.
      If node.HasChildNodes Then
         valueNode = node.ChildNodes(0)
         If (Not (valueNode) Is Nothing) Then
            Dim typeName As String = valueNode.Name
            If (propertyTypeName = typeName) Then
               ' The types are the same. 
               ' Replace the value of the node.
               valueNode.InnerText = propertyValueString
               ' If the property existed, and its type
               ' has not changed, you are finished.
               retVal = True
            Else
               ' Types are different. Delete the node
               ' and clear the node variable.
               node.ParentNode.RemoveChild(node)
               node = Nothing
            End If
         End If
      End If
   End If
   ' The previous block of code may have cleared the value in the 
   ' variable named node.
   If (node Is Nothing) Then
      ' Either you did not find the node, or you 
      ' found it, its type was incorrect, and you deleted it.
      ' Either way, you need to create the new property node now.
      ' Find the highest existing "pid" value.
      ' The default value for the "pid" attribute is "2".
      Dim pidValue As String = "2"
      Dim propertiesNode As XmlNode = customPropsDoc.DocumentElement
      If propertiesNode.HasChildNodes Then
         Dim lastNode As XmlNode = propertiesNode.LastChild
         If (Not (lastNode) Is Nothing) Then
            Dim pidAttr As XmlAttribute = lastNode.Attributes("pid")
            If Not (pidAttr Is Nothing) Then
               pidValue = pidAttr.Value
               ' Increment pidValue, so that the new property
               ' gets a pid value one higher. This value should be 
               ' numeric, but you should confirm that.
               Dim value As Integer = 0
               If Integer.TryParse(pidValue, value) Then
                  pidValue = Convert.ToString((value + 1))
               End If
            End If
         End If
      End If
      node = customPropsDoc.CreateElement("property", customPropertiesSchema)
      node.Attributes.Append(customPropsDoc.CreateAttribute("name"))
      node.Attributes("name").Value = propertyName
      node.Attributes.Append(customPropsDoc.CreateAttribute("fmtid"))
      node.Attributes("fmtid").Value = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"
      node.Attributes.Append(customPropsDoc.CreateAttribute("pid"))
      node.Attributes("pid").Value = pidValue
      valueNode = customPropsDoc.CreateElement(propertyTypeName, customVTypesSchema)
      valueNode.InnerText = propertyValueString
      node.AppendChild(valueNode)
      rootNode.AppendChild(node)
      retVal = True
   End If
   ' Save the properties XML back to its part.
   customPropsDoc.Save(customPropsPart.GetStream)

   Return retVal
End Function
public enum PropertyTypes
{
   YesNo,
   Text,
   DateTime,
   NumberInteger,
   NumberDouble,
}

public static bool WDSetCustomProperty(string docName, string propertyName, object propertyValue, PropertyTypes propertyType)
{
   // Given a document name, a property name/value, and the property type, add a custom property 
   // to a document. Return True if the property was added/updated, or False if the property cannot be updated.
   // The function's return value is true if the code could add/update the property,
   // and false otherwise.

   const string customPropertiesSchema = "http://schemas.liquid-technologies.com/OfficeOpenXML/2006/default.html";
   const string customVTypesSchema = "http://schemas.liquid-technologies.com/OfficeOpenXML/2006/default.html";

   bool retVal = false;
   string propertyTypeName = "vt:lpwstr";
   string propertyValueString = null;

   //  Calculate the correct type.
   switch (propertyType)
   {
      case PropertyTypes.DateTime:
         propertyTypeName = "vt:filetime";
         // Make sure you were passed a real date, 
         // and if so, format in the correct way. The date/time 
         // value passed in should represent a UTC date/time.
         if (propertyValue.GetType() == typeof(System.String))
         {
            propertyValueString = string.Format("{0:s}Z", Convert.ToDateTime(propertyValue));
         }
         break;
      case PropertyTypes.NumberInteger:
         propertyTypeName = "vt:i4";
         if (propertyValue.GetType() == typeof(System.Int32))
         {
            propertyValueString = Convert.ToInt32(propertyValue).ToString();
         }

         break;
      case PropertyTypes.NumberDouble:
         propertyTypeName = "vt:r8";
         if (propertyValue.GetType() == typeof(System.Double))
         {
            propertyValueString = Convert.ToDouble(propertyValue).ToString();
         }

         break;
      case PropertyTypes.Text:
         propertyTypeName = "vt:lpwstr";
         propertyValueString = Convert.ToString(propertyValue);

         break;
      case PropertyTypes.YesNo:
         propertyTypeName = "vt:bool";
         if (propertyValue.GetType() == typeof(System.Boolean))
         {
            // IMPORTANT: This value must be lowercase.
            propertyValueString = Convert.ToBoolean(propertyValue).ToString().ToLower();
         }
         break;
   }

   if (propertyValueString == null)
   {
      // If the code cannot convert the 
      // property to a valid value, throw an exception.
      throw new InvalidDataException("Invalid parameter value.");
   }

   using (WordprocessingDocument wdPackage = WordprocessingDocument.Open(docName, true))
   {

      // This part is working with the custom properties part.
      CustomFilePropertiesPart customPropsPart = wdPackage.CustomFilePropertiesPart;

      // You must manage namespaces to perform XML XPath queries.
      NameTable nt = new NameTable();
      XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
      nsManager.AddNamespace("d", customPropertiesSchema);
      nsManager.AddNamespace("vt", customVTypesSchema);

      Uri customPropsUri = new Uri("/docProps/custom.xml", UriKind.Relative);
      XmlDocument customPropsDoc = null;
      XmlNode rootNode = null;

      // There may not be a custom properties part.
      if (customPropsPart == null)
      {
         customPropsDoc = new XmlDocument(nt);

         // The part does not exist. Create it now.
         customPropsPart = wdPackage.AddCustomFilePropertiesPart();

         // Set up the rudimentary custom part.
         rootNode = customPropsDoc.CreateElement("Properties", customPropertiesSchema);
         rootNode.Attributes.Append(customPropsDoc.CreateAttribute("xmlns:vt"));
         rootNode.Attributes["xmlns:vt"].Value = customVTypesSchema;

         customPropsDoc.AppendChild(rootNode);
      }
      else
      {
         // Load the contents of the custom properties part into an XML document.
         customPropsDoc = new XmlDocument(nt);
         customPropsDoc.Load(customPropsPart.GetStream());
         rootNode = customPropsDoc.DocumentElement;
      }

      // Now that you have a reference to an XmlDocument object that 
      // corresponds to the custom properties part, 
      // check to see if the required property is already there.
      string searchString = string.Format("d:Properties/d:property[@name='{0}']", propertyName);
      XmlNode node = customPropsDoc.SelectSingleNode(searchString, nsManager);

      XmlNode valueNode = null;
      if (node != null)
      {
         // You found the node. Now check its type.
         if (node.HasChildNodes)
         {
            valueNode = node.ChildNodes[0];
            if (valueNode != null)
            {
               string typeName = valueNode.Name;
               if (propertyTypeName == typeName)
               {
                  // The types are the same. 
                  // Replace the value of the node.
                  valueNode.InnerText = propertyValueString;
                  // If the property existed, and its type
                  // has not changed, you are finished.
                  retVal = true;
               }
               else
               {
                  // Types are different. Delete the node
                  // and clear the node variable.
                  node.ParentNode.RemoveChild(node);
                  node = null;
               }
            }
         }
      }

      // The previous block of code may have cleared the value in the 
      // variable named node.
      if (node == null)
      {
         // Either you did not find the node, or you 
         // found it, its type was incorrect, and you deleted it.
         // Either way, you need to create the new property node now.

         // Find the highest existing "pid" value.
         // The default value for the "pid" attribute is "2".
         string pidValue = "2";

         XmlNode propertiesNode = customPropsDoc.DocumentElement;
         if (propertiesNode.HasChildNodes)
         {
            XmlNode lastNode = propertiesNode.LastChild;
            if (lastNode != null)
            {
               XmlAttribute pidAttr = lastNode.Attributes["pid"];
               if (!(pidAttr == null))
               {
                  pidValue = pidAttr.Value;
                  // Increment pidValue, so that the new property
                  // gets a pid value one higher. This value should be 
                  // numeric, but you should confirm that.
                  int value = 0;
                  if (int.TryParse(pidValue, out value))
                  {
                     pidValue = Convert.ToString(value + 1);
                  }
               }
            }
        }

            node = customPropsDoc.CreateElement("property", customPropertiesSchema);
            node.Attributes.Append(customPropsDoc.CreateAttribute("name"));
            node.Attributes["name"].Value = propertyName;

            node.Attributes.Append(customPropsDoc.CreateAttribute("fmtid"));
            node.Attributes["fmtid"].Value = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}";

            node.Attributes.Append(customPropsDoc.CreateAttribute("pid"));
            node.Attributes["pid"].Value = pidValue;

            valueNode = customPropsDoc.CreateElement(propertyTypeName, customVTypesSchema);
            valueNode.InnerText = propertyValueString;
            node.AppendChild(valueNode);
            rootNode.AppendChild(node);
            retVal = true;
         }

         // Save the properties XML back to its part.
         customPropsDoc.Save(customPropsPart.GetStream());
         //wdPackage.Save();
         }
   return retVal;
}

このコードは、最初に、可能性のあるカスタム プロパティの種類の列挙を定義します。

Public Enum PropertyTypes
   YesNo
   Text
   DateTime
   NumberInteger
   NumberDouble
End Enum
public enum PropertyTypes
{
   YesNo,
   Text,
   DateTime,
   NumberInteger,
   NumberDouble,
}

次に、このサンプル コードは WDSetCustomProperty を呼び出し、Word 2007 ドキュメントへの参照、カスタム プロパティの名前、プロパティを設定する新しい値、および列挙された値からプロパティの種類を渡します。

Dim propertyTypeName As String = "vt:lpwstr"
string propertyTypeName = "vt:lpwstr";

次に、ユーザーは propertyTypeName 変数を CustomFilePropertiesPart パーツの WordprocessingML マークアップの Text 値を表す既定のノード名 (vt:lpwstr) に設定します。ドキュメントのカスタム プロパティは、CustomFilePropertiesPart パーツにあります。最終的に、この変数はユーザーが設定するプロパティ値を含むノードを参照します。

次に、一連の Select Case ステートメント (Microsoft Visual C# の switch ステートメント) が更新するプロパティの種類をテストします。プロパティの値に応じて、コードはこの値を保持している特定のノードの名前と等しくなるように変数を設定します。次に、コードはプロパティを更新する値をフォーマットします。

Select Case (propertyType)
   Case PropertyTypes.DateTime
      propertyTypeName = "vt:filetime"
      If (TypeOf propertyValue Is String) Then
         propertyValueString = String.Format("{0:s}Z", Convert.ToDateTime(propertyValue))
      End If
   Case PropertyTypes.NumberInteger
      propertyTypeName = "vt:i4"
      If (TypeOf propertyValue Is Int32) Then
         propertyValueString = Convert.ToInt32(propertyValue).ToString
      End If
......
......
End Select
switch (propertyType)
{
   case PropertyTypes.DateTime:
      propertyTypeName = "vt:filetime";
      // Make sure you were passed a real date, 
      // and if so, format in the correct way. The date/time 
      // value passed in should represent a UTC date/time.
      if (propertyValue.GetType() == typeof(System.String))
      {
         propertyValueString = string.Format("{0:s}Z", Convert.ToDateTime(propertyValue));
      }
      break;
   case PropertyTypes.NumberInteger:
      propertyTypeName = "vt:i4";
      if (propertyValue.GetType() == typeof(System.Int32))
      {
         propertyValueString = Convert.ToInt32(propertyValue).ToString();
      }

      break;
.......
.......
}

次に、Office Open XML ファイル形式パッケージを表す WordprocessingDocument オブジェクトを入力ドキュメントに基づいて作成します。次に、コードは CustomFilePropertiesPart を取得します。次に、コードは XPath クエリをセットアップする名前空間マネージャを作成します。

コードの次のセクションは、カスタム プロパティ パーツが存在するかどうかを判定します。

If (customPropsPart Is Nothing) Then
   customPropsDoc = New XmlDocument(nt)
   ' The part does not exist. Create it now.
   customPropsPart = wdPackage.AddCustomFilePropertiesPart
   ' Set up the rudimentary custom part.
   rootNode = customPropsDoc.CreateElement("Properties", customPropertiesSchema)
   rootNode.Attributes.Append(customPropsDoc.CreateAttribute("xmlns:vt"))
   rootNode.Attributes("xmlns:vt").Value = customVTypesSchema
   customPropsDoc.AppendChild(rootNode)
Else
   ' Load the contents of the custom properties part into an XML document.
   customPropsDoc = New XmlDocument(nt)
   customPropsDoc.Load(customPropsPart.GetStream)
   rootNode = customPropsDoc.DocumentElement
End If
if (customPropsPart == null)
{
   customPropsDoc = new XmlDocument(nt);

   // The part does not exist. Create it now.
   customPropsPart = wdPackage.AddCustomFilePropertiesPart();

   // Set up the rudimentary custom part.
   rootNode = customPropsDoc.CreateElement("Properties", customPropertiesSchema);
   rootNode.Attributes.Append(customPropsDoc.CreateAttribute("xmlns:vt"));
   rootNode.Attributes["xmlns:vt"].Value = customVTypesSchema;

   customPropsDoc.AppendChild(rootNode);
}
else
{
   // Load the contents of the custom properties part into an XML document.
   customPropsDoc = new XmlDocument(nt);
   customPropsDoc.Load(customPropsPart.GetStream());
   rootNode = customPropsDoc.DocumentElement;
}

パーツが存在しない場合、コードはカスタム プロパティ パーツ シェルを作成し、基本プロパティを読み込みます。パーツが存在する場合は、ユーザーはその内容をメモリ常駐 XML ドキュメント内に読み込み、d:Properties/d:property ノードを検索する XPath クエリとして検索文字列をセットアップします。

If (Not (node) Is Nothing) Then
   ' You found the node. Now check its type.
   If node.HasChildNodes Then
      valueNode = node.ChildNodes(0)
      If (Not (valueNode) Is Nothing) Then
         Dim typeName As String = valueNode.Name
         If (propertyTypeName = typeName) Then
            ' The types are the same. 
            ' Replace the value of the node.
            valueNode.InnerText = propertyValueString
            ' If the property existed, and its type
            ' did not change, you are finished.
            retVal = True
         Else
            ' The types are different. Delete the node
            ' and clear the node variable.
            node.ParentNode.RemoveChild(node)
            node = Nothing
         End If
      End If
   End If
End If
if (node != null)
{
   // You found the node. Now check its type.
   if (node.HasChildNodes)
   {
      valueNode = node.ChildNodes[0];
      if (valueNode != null)
      {
         string typeName = valueNode.Name;
         if (propertyTypeName == typeName)
         {
            // The types are the same. 
            // Replace the value of the node.
            valueNode.InnerText = propertyValueString;
               // If the property existed, and its type
               // did not change, you are finished.
               retVal = true;
         }
         else
         {
             // Types are different. Delete the node
             // and clear the node variable.
             node.ParentNode.RemoveChild(node);
             node = null;
          }
      }
   }
}

このコードでは、以下のアクションが発生する可能性があります。

  • ノードが見つからなかった場合は、そのノードをパーツに追加します。

  • ノードが見つかり、種類が新しいプロパティとは異なる場合、そのノードを削除します。

  • ノードが見つかり、種類が新しいプロパティと同じ場合は、プロパティ値を置き換えます。そうでない場合、新しい値と種類を持つ新しいノードを追加します。

  • ノードが見つからなかった場合、または見つかって種類が適切ではない場合に、ノードを削除した場合、新しいプロパティ ノードを作成します。

Dim pidValue As String = "2"
Dim propertiesNode As XmlNode = customPropsDoc.DocumentElement
If propertiesNode.HasChildNodes Then
   Dim lastNode As XmlNode = propertiesNode.LastChild
   If (Not (lastNode) Is Nothing) Then
      Dim pidAttr As XmlAttribute = lastNode.Attributes("pid")
      If Not (pidAttr Is Nothing) Then
         pidValue = pidAttr.Value
         ' Increment pidValue, so that the new property
         ' gets a pid value one higher. This value should be 
         ' numeric, but you should confirm that.
         Dim value As Integer = 0
         If Integer.TryParse(pidValue, value) Then
                  pidValue = Convert.ToString((value + 1))
         End If
      End If
   End If
End If
string pidValue = "2";

XmlNode propertiesNode = customPropsDoc.DocumentElement;
if (propertiesNode.HasChildNodes)
{
   XmlNode lastNode = propertiesNode.LastChild;
   if (lastNode != null)
   {
      XmlAttribute pidAttr = lastNode.Attributes["pid"];
      if (!(pidAttr == null))
      {
         pidValue = pidAttr.Value;
         // Increment pidValue, so that the new property
         // gets a pid value one higher. This value should be 
         // numeric, but you should confirm that.
         int value = 0;
         if (int.TryParse(pidValue, out value))
         {
            pidValue = Convert.ToString(value + 1);
         }
      }
   }
}

各プロパティは、id 値 (pidValue) を持っていて、既定値は 2 です。この値は、既存のプロパティ id の値よりも 1 だけ大きくなければなりません。このコード セグメントは、既存のプロパティ ノード id の値 (存在する場合) を検索し、新しいプロパティ ノードの id が 1 だけ大きいことを確認します。

node = customPropsDoc.CreateElement("property", customPropertiesSchema)
node.Attributes.Append(customPropsDoc.CreateAttribute("name"))
node.Attributes("name").Value = propertyName
node.Attributes.Append(customPropsDoc.CreateAttribute("fmtid"))
node.Attributes("fmtid").Value = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"
node.Attributes.Append(customPropsDoc.CreateAttribute("pid"))
node.Attributes("pid").Value = pidValue
valueNode = customPropsDoc.CreateElement(propertyTypeName, customVTypesSchema)
valueNode.InnerText = propertyValueString
node.AppendChild(valueNode)
rootNode.AppendChild(node)
retVal = True
node = customPropsDoc.CreateElement("property", customPropertiesSchema);
node.Attributes.Append(customPropsDoc.CreateAttribute("name"));
node.Attributes["name"].Value = propertyName;

node.Attributes.Append(customPropsDoc.CreateAttribute("fmtid"));
node.Attributes["fmtid"].Value = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}";
node.Attributes.Append(customPropsDoc.CreateAttribute("pid"));
node.Attributes["pid"].Value = pidValue;
valueNode = customPropsDoc.CreateElement(propertyTypeName, customVTypesSchema);
valueNode.InnerText = propertyValueString;
node.AppendChild(valueNode);
rootNode.AppendChild(node);
retVal = true;

残りのコードは、プロパティ要素を作成し、その属性を追加し、ノードをルート ノードに追加します。最後の手順では、操作が正常に完了したかどうかを示す Boolean 値を返します。

ドキュメントの印刷方向を設定する

以下のコードは、ドキュメントの印刷方向を変更します。

Public Enum PrintOrientation
   Landscape
   Portrait
End Enum

Public Sub WDSetPrintOrientation(ByVal docName As String, ByVal newOrientation As PrintOrientation)
   ' Given a document name, set the print orientation for all the sections of the document.
   ' Example:
   ' WDSetPrintOrientation(@"C:\Samples\SetOrientation.docx", PrintOrientation.Landscape); 
   Const wordmlNamespace As String = "http://schemas.liquid-technologies.com/OfficeOpenXML/2006/default.html"
   Dim wdPackage As WordprocessingDocument = WordprocessingDocument.Open(docName, True)
   ' Get the officeDocument part.
   Dim documentPart As MainDocumentPart = wdPackage.MainDocumentPart
   ' Load the officeDocument part into an XML document.
   Dim doc As XmlDocument = New XmlDocument
   doc.Load(documentPart.GetStream)
   ' Manage namespaces to perform XPath queries.
   Dim nt As NameTable = New NameTable
   Dim nsManager As XmlNamespaceManager = New XmlNamespaceManager(nt)
   nsManager.AddNamespace("w", wordmlNamespace)
   Dim nodes As XmlNodeList = doc.SelectNodes("//w:sectPr/w:pgSz", nsManager)
   For Each node As System.Xml.XmlNode In nodes
      ' Retrieve the current orientation for the section.
      ' Assume the orientation is portrait.
      Dim orientation As PrintOrientation = PrintOrientation.Portrait
      Dim attr As XmlAttribute = node.Attributes("w:orient")
      If (Not (attr) Is Nothing) Then
         Select Case (attr.Value)
            Case "portrait"
               orientation = PrintOrientation.Portrait
            Case "landscape"
               orientation = PrintOrientation.Landscape
         End Select
      End If
      ' Compare the current orientation to the requested orientation.
      ' If it is the same, get out. Otherwise, make the changes.
      If (newOrientation <> orientation) Then
         If (attr Is Nothing) Then
            ' Create the attribute. Although this is not necessary
            ' when there is no change in orientation, 
            ' setting it has no negative effect.
            attr = node.Attributes.Append(doc.CreateAttribute("w:orient", wordmlNamespace))
         End If
         Select Case (newOrientation)
            Case PrintOrientation.Landscape
               attr.Value = "landscape"
            Case PrintOrientation.Portrait
               attr.Value = "portrait"
         End Select
         Dim pageSizeNode As XmlNode = node.ParentNode.SelectSingleNode("w:pgMar", nsManager)
         If (Not (pageSizeNode) Is Nothing) Then
            ' Swap page dimensions.
            Dim width As String = Nothing
            Dim height As String = Nothing
            Dim widthAttr As XmlAttribute = Nothing
            Dim heightAttr As XmlAttribute = Nothing
            widthAttr = node.Attributes("w:w")
            If (Not (widthAttr) Is Nothing) Then
               width = widthAttr.Value
            End If
            heightAttr = node.Attributes("w:h")
            If (Not (heightAttr) Is Nothing) Then
               height = heightAttr.Value
            End If
            If (Not (widthAttr) Is Nothing) Then
               widthAttr.Value = height
            End If
            If (Not (heightAttr) Is Nothing) Then
               heightAttr.Value = width
            End If
            ' Rotate margins. Printer settings determine how far you 
            ' rotate when switching to landscape mode. Not having those
            ' settings, this code rotates 90 degrees. You can 
            ' modify this behavior, or make it a parameter for the 
            ' procedure.
            Dim top As String = Nothing
            Dim bottom As String = Nothing
            Dim left As String = Nothing
            Dim right As String = Nothing
            Dim topAttr As XmlAttribute = Nothing
            Dim leftAttr As XmlAttribute = Nothing
            Dim bottomAttr As XmlAttribute = Nothing
            Dim rightAttr As XmlAttribute = Nothing
            topAttr = pageSizeNode.Attributes("w:top")
            If (Not (attr) Is Nothing) Then
               top = topAttr.Value
            End If
            leftAttr = pageSizeNode.Attributes("w:left")
            If (Not (attr) Is Nothing) Then
               left = leftAttr.Value
            End If
            rightAttr = pageSizeNode.Attributes("w:right")
            If (Not (attr) Is Nothing) Then
               right = rightAttr.Value
            End If
            bottomAttr = pageSizeNode.Attributes("w:bottom")
            If (Not (attr) Is Nothing) Then
               bottom = bottomAttr.Value
            End If
            If (Not (topAttr) Is Nothing) Then
               topAttr.Value = left
            End If
            If (Not (leftAttr) Is Nothing) Then
               leftAttr.Value = bottom
            End If
            If (Not (rightAttr) Is Nothing) Then
               rightAttr.Value = top
            End If
            If (Not (bottomAttr) Is Nothing) Then
               bottomAttr.Value = right
            End If
         End If
      End If
   Next
   ' Save the document XML back to its part.
   doc.Save(documentPart.GetStream)
End Sub
public enum PrintOrientation
{
   Landscape,
   Portrait,
}

public static void WDSetPrintOrientation(string docName, PrintOrientation newOrientation)
{
   // Given a document name, set the print orientation for all the sections of the document.

   // Example:
   // WDSetPrintOrientation(@"C:\Samples\SetOrientation.docx", PrintOrientation.Landscape); 

   const string wordmlNamespace = "http://schemas.liquid-technologies.com/OfficeOpenXML/2006/default.html";

   using (WordprocessingDocument wdPackage = WordprocessingDocument.Open(docName, true))
   {
      // Get the officeDocument part.
      MainDocumentPart documentPart = wdPackage.MainDocumentPart;

      // Load the officeDocument part into an XML document.
      XmlDocument doc = new XmlDocument();
      doc.Load(documentPart.GetStream());

      // Manage namespaces to perform XPath queries.
      NameTable nt = new NameTable();
      XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
      nsManager.AddNamespace("w", wordmlNamespace);

      XmlNodeList nodes = doc.SelectNodes("//w:sectPr/w:pgSz", nsManager);
      foreach (System.Xml.XmlNode node in nodes)
      {
         // Retrieve the current orientation for the section.
         // Assume the orientation is portrait.
         PrintOrientation orientation = PrintOrientation.Portrait;
         XmlAttribute attr = node.Attributes["w:orient"];
         if (attr != null)
         {
            switch (attr.Value)
            {
               case "portrait":
                  orientation = PrintOrientation.Portrait;
                  break;
               case "landscape":
                  orientation = PrintOrientation.Landscape;
                  break;
            }
         }

         // Compare the current orientation to the requested orientation.
         // If it is the same, get out. Otherwise, make the changes.
         if (newOrientation != orientation)
         {
            if (attr == null)
            {
               // Create the attribute. Although this is not necessary
               // when there is no change in orientation, 
               // setting it has no negative effect.
               attr = node.Attributes.Append(doc.CreateAttribute("w:orient", wordmlNamespace));
            }
            switch (newOrientation)
            {
               case PrintOrientation.Landscape:
                  attr.Value = "landscape";
                  break;
               case PrintOrientation.Portrait:
                  attr.Value = "portrait";
                  break;
            }

            XmlNode pageSizeNode = node.ParentNode.SelectSingleNode("w:pgMar", nsManager);
            if (pageSizeNode != null)
            {
               // Swap page dimensions.
               string width = null;
               string height = null;
               XmlAttribute widthAttr = null;
               XmlAttribute heightAttr = null;

               widthAttr = node.Attributes["w:w"];
               if (widthAttr != null)
               {
                  width = widthAttr.Value;
               }

               heightAttr = node.Attributes["w:h"];
               if (heightAttr != null)
               {
                  height = heightAttr.Value;
               }

               if (widthAttr != null)
               {
                  widthAttr.Value = height;
               }

               if (heightAttr != null)
               {
                  heightAttr.Value = width;
               }

               // Rotate margins. Printer settings determine how far you 
               // rotate when switching to landscape mode. Not having those
               // settings, this code rotates 90 degrees. You can 
               // modify this behavior, or make it a parameter for the 
               // procedure.
               string top = null;
               string bottom = null;
               string left = null;
               string right = null;

               XmlAttribute topAttr = null;
               XmlAttribute leftAttr = null;
               XmlAttribute bottomAttr = null;
               XmlAttribute rightAttr = null;

               topAttr = pageSizeNode.Attributes["w:top"];
               if (attr != null)
               {
                  top = topAttr.Value;
               }
               leftAttr = pageSizeNode.Attributes["w:left"];
               if (attr != null)
               {
                  left = leftAttr.Value;
               }
               rightAttr = pageSizeNode.Attributes["w:right"];
               if (attr != null)
               {
                  right = rightAttr.Value;
               }
               bottomAttr = pageSizeNode.Attributes["w:bottom"];
               if (attr != null)
               {
                  bottom = bottomAttr.Value;
               }

               if (topAttr != null)
               {
                  topAttr.Value = left;
               }
               if (leftAttr != null)
               {
                  leftAttr.Value = bottom;
               }
               if (rightAttr != null)
               {
                  rightAttr.Value = top;
               }
               if (bottomAttr != null)
               {
                  bottomAttr.Value = right;
               }
            }
         }
      }

   // Save the document XML back to its part.
   doc.Save(documentPart.GetStream());
   }
}

このコードは、最初に 2 つの印刷オプションの列挙を定義します。

Public Enum PrintOrientation
   Landscape
   Portrait
End Enum
public enum PrintOrientation
{
   Landscape,
   Portrait,
}

次に、コードは WDSetPrintOrientation を呼び出し、Word 2007 ドキュメントへの参照および必要な印刷方向 (横または縦) を渡します。次に、ユーザーは Office Open XML Format パッケージを表す WordprocessingDocument オブジェクトをセットアップし、MainDocumentPart パーツへの参照を設定します。ユーザーは、メモリ常駐 XML ドキュメントを作成し、メイン ドキュメント パーツの内容を読み込みます。

次に、ユーザーは XmlNamespaceManager オブジェクトを使用し、w 修飾子を使用して、既定の WordprocessingML 名前空間への参照を設定することで名前空間マネージャを設定します。次に、ユーザーは次の XPath 式を使用してプリンタ固有のノードを選択します。

Dim nodes As XmlNodeList = doc.SelectNodes("//w:sectPr/w:pgSz", nsManager)
XmlNodeList nodes = doc.SelectNodes("//w:sectPr/w:pgSz", nsManager);

次に、ユーザーは w:orient ノードをテストして現在の設定を判定します。このプロシージャは、縦向きを前提としています。

Dim attr As XmlAttribute = node.Attributes("w:orient")
If (Not (attr) Is Nothing) Then
   Select Case (attr.Value)
      Case "portrait"
          orientation = PrintOrientation.Portrait
      Case "landscape"
          orientation = PrintOrientation.Landscape
   End Select
End If
XmlAttribute attr = node.Attributes["w:orient"];
if (attr != null)
{
   switch (attr.Value)
   {
       case "portrait":
          orientation = PrintOrientation.Portrait;
          break;
       case "landscape":
          orientation = PrintOrientation.Landscape;
          break;
   }
}

次に、プロシージャは要求された向きが現在の向きと同じかどうかを確認し、同じ場合は終了します。

If (newOrientation <> orientation) Then
if (newOrientation != orientation)

そうでない場合、コードの残りの部分はドキュメントの向きを変えるために必要な印刷属性を変更します。

まとめ

この記事で説明したように、「Microsoft SDK for Open XML Formats テクノロジ プレビュー」を参照することで、Word 2007 ファイルの操作が非常に簡単になります。この一連の記事のコードを習得し、Office Open XML オブジェクト モデルを使用してユーザー独自のプログラミングの問題を解決してください。

追加情報

詳細については、以下のリソースを参照してください。