Partager via


Définir une propriété personnalisée dans un document de traitement de texte

Cette rubrique montre comment utiliser les classes du Kit de développement logiciel (SDK) Open XML pour Office afin de définir par programmation une propriété personnalisée dans un document de traitement de texte. Il contient un exemple SetCustomProperty de méthode pour illustrer cette tâche.

L'exemple de code inclut également une énumération qui définit les types possibles de propriétés personnalisées. La SetCustomProperty méthode nécessite que vous fournissiez l’une de ces valeurs lorsque vous appelez la méthode .

enum PropertyTypes : int
{
    YesNo,
    Text,
    DateTime,
    NumberInteger,
    NumberDouble
}

Mode de stockage des propriétés personnalisées

Il est important de comprendre comment les propriétés personnalisées sont stockées dans un document de traitement de texte. Vous pouvez utiliser l’outil de productivité pour Microsoft Office, illustré dans la figure 1, pour découvrir comment ils sont stockés. Cet outil vous permet d'ouvrir un document et de voir ses parties et la hiérarchie de celles-ci. La figure 1 montre un document de test après l'exécution du code dans la section Appel de la méthode SetCustomProperty de cet article. L'outil affiche dans les volets de droite le XML de la partie et le code C# correspondant que vous pouvez utiliser pour générer le contenu de la partie.

Figure 1. Outil de productivité du Kit de développement logiciel (SDK) Open XML pour Microsoft Office

Outil de productivité du Kit de développement logiciel (SDK) Open XML

Le XML correspondant est également extrait et présenté pour simplifier la lecture.

    <op:Properties xmlns:vt="https://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes" xmlns:op="https://schemas.openxmlformats.org/officeDocument/2006/custom-properties">
      <op:property fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}" pid="2" name="Manager">
        <vt:lpwstr>Mary</vt:lpwstr>
      </op:property>
      <op:property fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}" pid="3" name="ReviewDate">
        <vt:filetime>2010-12-21T00:00:00Z</vt:filetime>
      </op:property>
    </op:Properties>

Si vous examinez le contenu XML, vous noterez les points suivants :

  • Chaque propriété du contenu XML est composée d'un élément XML qui inclut le nom et la valeur de la propriété.
  • Pour chaque propriété, le contenu XML inclut un fmtid attribut, qui est toujours défini sur la même valeur de chaîne : {D5CDD505-2E9C-101B-9397-08002B2CF9AE}.
  • Chaque propriété du contenu XML inclut un pid attribut, qui doit inclure un entier commençant à 2 pour la première propriété et incrémentant pour chaque propriété successive.
  • Chaque propriété effectue le suivi de son type (dans la figure, les vt:lpwstr noms d’éléments et vt:filetime définissent les types pour chaque propriété).

L’exemple de méthode fourni ici inclut le code requis pour créer ou modifier une propriété de document personnalisée dans un document Microsoft Word. Vous pouvez facilement trouver le listing du code complet pour la méthode dans la section Exemple de code.

Méthode SetCustomProperty

Utilisez la SetCustomProperty méthode pour définir une propriété personnalisée dans un document de traitement de texte. La SetCustomProperty méthode accepte quatre paramètres :

  • Le nom du document à modifier (chaîne).

  • Le nom de la propriété à ajouter ou à modifier (chaîne).

  • La valeur de la propriété (objet).

  • Type de propriété (une des valeurs de l’énumération PropertyTypes ).

static string SetCustomProperty(
    string fileName,
    string propertyName,
    object propertyValue,
    PropertyTypes propertyType)

Appel de la méthode SetCustomProperty

La SetCustomProperty méthode vous permet de définir une propriété personnalisée et retourne la valeur actuelle de la propriété, si elle existe. Pour appeler l'exemple de méthode, passez le nom du fichier, le nom de la propriété, la valeur de la propriété et les paramètres de type de propriété. Le code suivant est fourni à titre d'exemple.

string fileName = args[0];

Console.WriteLine(string.Join("Manager = ", SetCustomProperty(fileName, "Manager", "Pedro", PropertyTypes.Text)));

Console.WriteLine(string.Join("Manager = ", SetCustomProperty(fileName, "Manager", "Bonnie", PropertyTypes.Text)));

Console.WriteLine(string.Join("ReviewDate = ", SetCustomProperty(fileName, "ReviewDate", DateTime.Parse("01/26/2024"), PropertyTypes.DateTime)));

Après l'exécution de ce code, utilisez la procédure suivante pour afficher les propriétés personnalisées dans Word.

  1. Ouvrez le fichier .docx dans Word.
  2. Dans l'onglet Fichier, cliquez sur Info.
  3. Cliquez sur Propriétés.
  4. Cliquez sur Propriétés avancées.

Les propriétés personnalisées s'affichent dans la boîte de dialogue qui apparaît, comme le montre la figure 2.

Figure 2. Propriétés personnalisées dans la boîte de dialogue Propriétés avancées

Boîte de dialogue Propriétés avancées avec des propriétés personnalisées

Fonctionnement du code

La SetCustomProperty méthode commence par configurer certaines variables internes. Ensuite, il examine les informations sur la propriété et crée un en CustomDocumentProperty fonction des paramètres que vous avez spécifiés. Le code maintient également une variable nommée propSet pour indiquer s'il a réussi ou non à créer le nouvel objet de propriété. Ce code vérifie le type de la valeur de propriété, puis convertit l’entrée en type correct, en définissant la propriété appropriée de l’objet CustomDocumentProperty .

Remarque

Le CustomDocumentProperty type fonctionne comme un type variant VBA. Il maintient des espaces réservés séparés comme propriétés pour les divers types de données qu'il pourrait contenir.

string? returnValue = string.Empty;

var newProp = new CustomDocumentProperty();
bool propSet = false;


string? propertyValueString = propertyValue.ToString() ?? throw new System.ArgumentNullException("propertyValue can't be converted to a string.");

// Calculate the correct type.
switch (propertyType)
{
    case PropertyTypes.DateTime:

        // Be 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) is DateTime)
        {
            newProp.VTFileTime =
                new VTFileTime(string.Format("{0:s}Z",
                    Convert.ToDateTime(propertyValue)));
            propSet = true;
        }

        break;

    case PropertyTypes.NumberInteger:
        if ((propertyValue) is int)
        {
            newProp.VTInt32 = new VTInt32(propertyValueString);
            propSet = true;
        }

        break;

    case PropertyTypes.NumberDouble:
        if (propertyValue is double)
        {
            newProp.VTFloat = new VTFloat(propertyValueString);
            propSet = true;
        }

        break;

    case PropertyTypes.Text:
        newProp.VTLPWSTR = new VTLPWSTR(propertyValueString);
        propSet = true;

        break;

    case PropertyTypes.YesNo:
        if (propertyValue is bool)
        {
            // Must be lowercase.
            newProp.VTBool = new VTBool(
              Convert.ToBoolean(propertyValue).ToString().ToLower());
            propSet = true;
        }
        break;
}

if (!propSet)
{
    // If the code was not able to convert the 
    // property to a valid value, throw an exception.
    throw new InvalidDataException("propertyValue");
}

À ce stade, si le code n’a pas levée d’exception, vous pouvez supposer que la propriété est valide et que le code définit les FormatId propriétés et Name de la nouvelle propriété personnalisée.

// Now that you have handled the parameters, start
// working on the document.
newProp.FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}";
newProp.Name = propertyName;

Travail sur le document

Étant donné l’objet CustomDocumentProperty , le code interagit ensuite avec le document que vous avez fourni dans les paramètres de la SetCustomProperty procédure. Le code commence par ouvrir le document en mode lecture/écriture à l’aide de la Open méthode de la WordprocessingDocument classe . Le code tente de récupérer une référence à la partie des propriétés de fichier personnalisées à l’aide de la CustomFilePropertiesPart propriété du document.

using (var document = WordprocessingDocument.Open(fileName, true))
{
    var customProps = document.CustomFilePropertiesPart;

Si le code ne peut pas trouver une partie de propriétés personnalisées, il crée une nouvelle partie, puis y ajoute un nouvel ensemble de propriétés.

if (customProps is null)
{
    // No custom properties? Add the part, and the
    // collection of properties now.
    customProps = document.AddCustomFilePropertiesPart();
    customProps.Properties = new Properties();
}

Ensuite, le code récupère une référence à la Properties propriété de la partie de propriétés personnalisées (autrement dit, une référence aux propriétés elles-mêmes). Si le code doit créer une nouvelle partie de propriétés personnalisées, vous savez que cette référence n'est pas nulle. Toutefois, pour les parties de propriétés personnalisées existantes, il est possible, bien que très improbable, que la Properties propriété soit null. Si c'est le cas, le code ne peut pas continuer.

var props = customProps.Properties;

if (props is not null)
{

Si la propriété existe déjà, le code récupère sa valeur actuelle, puis supprime la propriété. Pourquoi supprimer la propriété ? Si le nouveau type pour la propriété correspond au type existant pour la propriété, le code pourrait définir la valeur de la propriété à une nouvelle valeur. En revanche, si le nouveau type ne correspond pas, le code doit créer un nouvel élément, supprimant l'ancien (c'est le nom de l'élément qui définit son type, pour plus d'informations, voir la figure 1). Il est plus simple de toujours supprimer, puis recréer l'élément. Le code utilise une requête LINQ simple pour trouver la première correspondance pour le nom de la propriété.

var prop = props.FirstOrDefault(p => ((CustomDocumentProperty)p).Name!.Value == propertyName);

// Does the property exist? If so, get the return value, 
// and then delete the property.
if (prop is not null)
{
    returnValue = prop.InnerText;
    prop.Remove();
}

Maintenant, vous savez de façon certaine que la partie de propriétés personnalisées existe, qu'une propriété ayant le même nom que la nouvelle propriété n'existe pas, et que et qu'il pourrait y avoir d'autres propriétés personnalisées existantes. Le code effectue les opérations suivantes :

  1. Ajoute la nouvelle propriété en tant qu'enfant de la collection de propriétés.

  2. Effectue une boucle sur toutes les propriétés existantes et définit l’attribut sur des pid valeurs croissantes, en commençant à 2.

  3. Enregistre la partie.

// Append the new property, and 
// fix up all the property ID values. 
// The PropertyId value must start at 2.
props.AppendChild(newProp);
int pid = 2;
foreach (CustomDocumentProperty item in props)
{
    item.PropertyId = pid++;
}

Enfin, le code renvoie la valeur de propriété d'origine stockée.

return returnValue;

Exemple de code

Voici l’exemple de code complet SetCustomProperty en C# et Visual Basic.

static string SetCustomProperty(
    string fileName,
    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. The method returns the original
    // value, if it existed.


    string? returnValue = string.Empty;

    var newProp = new CustomDocumentProperty();
    bool propSet = false;


    string? propertyValueString = propertyValue.ToString() ?? throw new System.ArgumentNullException("propertyValue can't be converted to a string.");

    // Calculate the correct type.
    switch (propertyType)
    {
        case PropertyTypes.DateTime:

            // Be 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) is DateTime)
            {
                newProp.VTFileTime =
                    new VTFileTime(string.Format("{0:s}Z",
                        Convert.ToDateTime(propertyValue)));
                propSet = true;
            }

            break;

        case PropertyTypes.NumberInteger:
            if ((propertyValue) is int)
            {
                newProp.VTInt32 = new VTInt32(propertyValueString);
                propSet = true;
            }

            break;

        case PropertyTypes.NumberDouble:
            if (propertyValue is double)
            {
                newProp.VTFloat = new VTFloat(propertyValueString);
                propSet = true;
            }

            break;

        case PropertyTypes.Text:
            newProp.VTLPWSTR = new VTLPWSTR(propertyValueString);
            propSet = true;

            break;

        case PropertyTypes.YesNo:
            if (propertyValue is bool)
            {
                // Must be lowercase.
                newProp.VTBool = new VTBool(
                  Convert.ToBoolean(propertyValue).ToString().ToLower());
                propSet = true;
            }
            break;
    }

    if (!propSet)
    {
        // If the code was not able to convert the 
        // property to a valid value, throw an exception.
        throw new InvalidDataException("propertyValue");
    }

    // Now that you have handled the parameters, start
    // working on the document.
    newProp.FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}";
    newProp.Name = propertyName;

    using (var document = WordprocessingDocument.Open(fileName, true))
    {
        var customProps = document.CustomFilePropertiesPart;

        if (customProps is null)
        {
            // No custom properties? Add the part, and the
            // collection of properties now.
            customProps = document.AddCustomFilePropertiesPart();
            customProps.Properties = new Properties();
        }

        var props = customProps.Properties;

        if (props is not null)
        {

            // This will trigger an exception if the property's Name 
            // property is null, but if that happens, the property is damaged, 
            // and probably should raise an exception.

            var prop = props.FirstOrDefault(p => ((CustomDocumentProperty)p).Name!.Value == propertyName);

            // Does the property exist? If so, get the return value, 
            // and then delete the property.
            if (prop is not null)
            {
                returnValue = prop.InnerText;
                prop.Remove();
            }

            // Append the new property, and 
            // fix up all the property ID values. 
            // The PropertyId value must start at 2.
            props.AppendChild(newProp);
            int pid = 2;
            foreach (CustomDocumentProperty item in props)
            {
                item.PropertyId = pid++;
            }
        }
    }

    return returnValue;
}

Voir aussi