Compartilhar via


Definir uma propriedade personalizada em um documento de processamento de texto

Este tópico mostra como utilizar as classes no SDK Open XML para o Office para definir programaticamente uma propriedade personalizada num documento de processamento de palavras. Contém um método de exemplo SetCustomProperty para ilustrar esta tarefa.

O código de exemplo também inclui uma enumeração que define os possíveis tipos de propriedades personalizadas. O SetCustomProperty método requer que forneça um destes valores quando chamar o método .

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

Como são armazenadas as propriedades personalizadas

É importante compreender como as propriedades personalizadas são armazenadas num documento de processamento de palavras. Pode utilizar a Ferramenta de Produtividade para o Microsoft Office, apresentada na Figura 1, para descobrir como são armazenadas. Esta ferramenta permite-lhe abrir um documento e ver as respetivas partes e a hierarquia de peças. A Figura 1 mostra um documento de teste depois de executar o código na secção Chamar o Método SetCustomProperty deste artigo. A ferramenta é apresentada nos painéis da direita, tanto o XML da peça como o código C# refletido que pode utilizar para gerar os conteúdos da peça.

Figura 1. Open XML SDK Productivity Tool for Microsoft Office

Open XML SDK Productivity Tool

O XML relevante também é extraído e mostrado aqui para facilitar a leitura.

    <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>

Se examinar o conteúdo XML, encontrará o seguinte:

  • Cada propriedade no conteúdo XML consiste num elemento XML que inclui o nome e o valor da propriedade.
  • Para cada propriedade, o conteúdo XML inclui um fmtid atributo, que está sempre definido para o mesmo valor de cadeia: {D5CDD505-2E9C-101B-9397-08002B2CF9AE}.
  • Cada propriedade no conteúdo XML inclui um pid atributo, que tem de incluir um número inteiro a partir de 2 para a primeira propriedade e incremento para cada propriedade sucessiva.
  • Cada propriedade controla o respetivo tipo (na figura, os nomes dos vt:lpwstr elementos e vt:filetime definem os tipos para cada propriedade).

O método de exemplo fornecido aqui inclui o código necessário para criar ou modificar uma propriedade de documento personalizado num documento do Microsoft Word. Pode encontrar a listagem de código completa do método na secção Código de Exemplo .

Método SetCustomProperty

Utilize o SetCustomProperty método para definir uma propriedade personalizada num documento de processamento de palavras. O SetCustomProperty método aceita quatro parâmetros:

  • O nome do documento a modificar (cadeia).

  • O nome da propriedade a adicionar ou modificar (cadeia).

  • O valor da propriedade (objeto).

  • O tipo de propriedade (um dos valores na PropertyTypes enumeração).

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

Chamar o Método SetCustomProperty

O SetCustomProperty método permite-lhe definir uma propriedade personalizada e devolve o valor atual da propriedade, se existir. Para chamar o método de exemplo, transmita o nome do ficheiro, o nome da propriedade, o valor da propriedade e os parâmetros do tipo de propriedade. O código de exemplo seguinte mostra um exemplo.

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)));

Depois de executar este código, utilize o seguinte procedimento para ver as propriedades personalizadas de Word.

  1. Abra o ficheiro de .docx no Word.
  2. No separador Ficheiro , clique em Informações.
  3. Clique em Propriedades.
  4. Clique em Propriedades Avançadas.

As propriedades personalizadas serão apresentadas na caixa de diálogo apresentada, conforme mostrado na Figura 2.

Figura 2. Propriedades Personalizadas na caixa de diálogo Propriedades Avançadas

Advanced Properties dialog with custom properties

Como Funciona o Código

O SetCustomProperty método começa por configurar algumas variáveis internas. Em seguida, examina as informações sobre a propriedade e cria uma nova CustomDocumentProperty com base nos parâmetros que especificou. O código também mantém uma variável com o nome propSet para indicar se criou com êxito o novo objeto de propriedade. Este código verifica o tipo do valor da propriedade e, em seguida, converte a entrada para o tipo correto, definindo a propriedade adequada do CustomDocumentProperty objeto.

Observação

O CustomDocumentProperty tipo funciona como um tipo de Variante VBA. Mantém marcadores de posição separados como propriedades para os vários tipos de dados que pode conter.

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");
}

Neste momento, se o código não tiver emitido uma exceção, pode assumir que a propriedade é válida e o código define as FormatId propriedades e Name da nova propriedade personalizada.

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

Trabalhar com o Documento

Dado o CustomDocumentProperty objeto, o código interage em seguida com o documento que forneceu nos parâmetros do SetCustomProperty procedimento. O código começa por abrir o documento no modo de leitura/escrita com o Open método da WordprocessingDocument classe . O código tenta obter uma referência à parte das propriedades do ficheiro personalizado com a CustomFilePropertiesPart propriedade do documento.

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

Se o código não conseguir localizar uma parte de propriedades personalizadas, cria uma nova parte e adiciona um novo conjunto de propriedades à peça.

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

Em seguida, o código obtém uma referência à Properties propriedade da parte das propriedades personalizadas (ou seja, uma referência às próprias propriedades). Se o código tiver de criar uma nova peça de propriedades personalizadas, sabe que esta referência não é nula. No entanto, para as partes de propriedades personalizadas existentes, é possível, embora altamente improvável, que a Properties propriedade seja nula. Se for o caso, o código não pode continuar.

var props = customProps.Properties;

if (props is not null)
{

Se a propriedade já existir, o código obtém o valor atual e, em seguida, elimina a propriedade. Porquê eliminar a propriedade? Se o novo tipo da propriedade corresponder ao tipo existente da propriedade, o código poderá definir o valor da propriedade para o novo valor. Por outro lado, se o novo tipo não corresponder, o código tem de criar um novo elemento, eliminando o antigo (é o nome do elemento que define o respetivo tipo— para obter mais informações, consulte a Figura 1). É mais simples eliminar sempre e, em seguida, recriar o elemento. O código utiliza uma consulta LINQ simples para encontrar a primeira correspondência para o nome da propriedade.

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();
}

Agora, terá a certeza de que a parte da propriedade personalizada existe, uma propriedade que tem o mesmo nome que a nova propriedade não existe e que podem existir outras propriedades personalizadas existentes. O código executa os seguintes passos:

  1. Acrescenta a nova propriedade como subordinada da coleção de propriedades.

  2. Percorre todas as propriedades existentes e define o pid atributo para aumentar os valores, a partir de 2.

  3. Guarda a peça.

// 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++;
}

Por fim, o código devolve o valor de propriedade original armazenado.

return returnValue;

Código de exemplo

Segue-se o exemplo de código completo SetCustomProperty em C# e 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;
}

Confira também