1

I have a Word VBA macro that I am converting to C# using the Office.Interop.

The code works nicely and I was able to convert everything, but I got stuck with trying to read to number of pages from the BuiltInDocumentProperties.

No matter what cast I use, it still does not work and returns null.

Here is the converted code:

using Word = Microsoft.Office.Interop.Word;
using Microsoft.Office;
using Microsoft.Office.Interop;
using System.Diagnostics;
using System.Reflection;

Word.Application oWord = new Word.Application();
Word.Document oTgtDoc = new Word.Document();

var PgNum = oTgtDoc.BuiltInDocumentProperties["Number of Pages"];

float intWidthCount = engColWidth;

while (true)
{
    oTgtDoc.Tables[1].Columns[1].SetWidth(intWidthCount, Word.WdRulerStyle.wdAdjustProportional);      

    intWidthCount += 5;
    oTgtDoc.Repaginate();
    oWord.Application.ScreenRefresh();

    if (oTgtDoc.BuiltInDocumentProperties["Number of Pages"] > PgNum && intWidthCount > engColWidth)
    {
        while (oTgtDoc.BuiltInDocumentProperties["Number of Pages"] > PgNum)
        {
            intWidthCount--;
            oTgtDoc.Tables[1].Columns[1].SetWidth(intWidthCount, Word.WdRulerStyle.wdAdjustProportional); 

            oTgtDoc.Repaginate();
            oWord.Application.ScreenRefresh();
        }
        break;
    } 
    else
    {
      PgNum = oTgtDoc.BuiltInDocumentProperties["Number of Pages"];
    }

I have looked at several other post and msdn and did not get to the right solution yet. For example this one: Accessing document properties - Excel Workbook/CSV in VB

Or this one with the Dictionary, which would not work as I need to access it several times in the while loop: Read BuiltInDocumentProperties/CustomDocumentProperties alway null with Word 2010?

Any suggestions how to access this BuiltInDocumentProperties["Number of Pages"] from my C# code?

Community
  • 1
  • 1
ib11
  • 2,530
  • 3
  • 22
  • 55

2 Answers2

1

Does it have to be from BuiltInDocumentProperties?

You could try this:

// get number of pages
Microsoft.Office.Interop.Word.WdStatistic stat = Microsoft.Office.Interop.Word.WdStatistic.wdStatisticPages;
int pages = doc.ComputeStatistics(stat, Type.Missing);

Copied from this answer: How to get page number?

More info on Word statistics from MSDN

Community
  • 1
  • 1
Arina
  • 91
  • 1
  • 8
  • 1
    This one is also really good. In fact a simpler rendition: `PgNum = oTgtDoc.ComputeStatistics(Word.WdStatistic.wdStatisticPages, Type.Missing);` Since I already have a `using Word = Microsoft.Office.Interop.Word;` And this one does not event need to be cast. I wonder which one is actually faster, this and the other one above seem to be pretty fast. Would be interesting to time it. I believe I will choose your version, because of the simplicity of the code. – ib11 May 14 '16 at 04:43
1

edited: added example of using var PgNum = oTgtDoc.BuiltInDocumentProperties(Word.WdBuiltInProperty.wdPropertyPages).Value.ToString(); before "Reflection" example

edited 2: to take account for OP's need to use PgNum as an int

you must use

 var PgNum = oTgtDoc.BuiltInDocumentProperties(Word.WdBuiltInProperty.wdPropertyPages).Value;

or

 int PgNum = oTgtDoc.BuiltInDocumentProperties(Word.WdBuiltInProperty.wdPropertyPages).Value;

and BTW

Word.Document oTgtDoc = new Word.Document();

won't return any new Word document (i.e. a 'real' new document in Word UI) but just a new word document object in your class

should you actually want to have a new blank UI Word document than you'd use:

    object oMissing = Missing.Value;
    Word.Document oTgtDoc = oWord.Documents.Add(ref oMissing, ref oMissing, ref oMissing, ref oMissing);

here's how I tested var PgNum = oTgtDoc.BuiltInDocumentProperties(Word.WdBuiltInProperty.wdPropertyPages).Value.ToString(); with a console application

using System;
using System.Reflection;
using Word = Microsoft.Office.Interop.Word;


namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {    
            RunWordExample();
        } 


        static void RunWordExample()
        {
            Word.Application oWord = new Word.Application(); //Create an instance of Microsoft Word

            //Create a new Document
            object oMissing = Missing.Value;
            Word.Document oTgtDoc = oWord.Documents.Add(ref oMissing, ref oMissing, ref oMissing, ref oMissing);

            //make Word visible
            oWord.Visible = true;

            // get number of pages #1
            var PgNum = oTgtDoc.BuiltInDocumentProperties(Word.WdBuiltInProperty.wdPropertyPages).Value.ToString();
            Console.WriteLine("doc {0} has {1} page(s)", oTgtDoc.Name, PgNum);
            Console.ReadLine();

        }

    }
}

Finally, by drilling down the links you provided I think it could be useful to build a helper class and learn a bit about how Reflection works by exploiting BuiltInDocumentProperties object

using System;
using Word = Microsoft.Office.Interop.Word;
using System.Reflection;

namespace WordHelpers
{
    class MyWordHelpers
    {   
        public static string GetBuiltInDocumentProperty(Word.Document oDoc, string propertyName)
        {
            object oDocBuiltInProps;

            oDocBuiltInProps = oDoc.BuiltInDocumentProperties;
            Type typeDocBuiltInProps = oDocBuiltInProps.GetType();

            //get the property
            object oDocAuthorProp = typeDocBuiltInProps.InvokeMember("Item",
                                       BindingFlags.Default |
                                       BindingFlags.GetProperty,
                                       null, oDocBuiltInProps,
                                       new object[] { propertyName }); // <-- here you exploit the passed property 

            //get the property type
            Type typeDocAuthorProp = oDocAuthorProp.GetType();

            // return the property value
            return typeDocAuthorProp.InvokeMember("Value",
                                       BindingFlags.Default |
                                       BindingFlags.GetProperty,
                                       null, oDocAuthorProp,
                                       new object[] { }).ToString(); ;
        }
    }

}

to be used as follows (from a console application)

static void RunWordExample()
{
    Word.Application oWord = new Word.Application(); //Create an instance of Microsoft Word

    //Create a new Document
    object oMissing = Missing.Value;
    Word.Document oTgtDoc = oWord.Documents.Add(ref oMissing, ref oMissing, ref oMissing, ref oMissing);

    //make Word visible
    oWord.Visible = true;

    // get number of pages
    Console.WriteLine("doc {0} has {1} page(s)", oTgtDoc.Name, MyWordHelpers.GetBuiltInDocumentProperty(oTgtDoc,"Number of Pages"));
    Console.ReadLine();

}
user3598756
  • 28,893
  • 4
  • 18
  • 28
  • Thanks. RE: **"you must use `var PgNum = oTgtDoc.BuiltInDocumentProperties(Word.WdBuiltInProperty.wdPropertyPages).Value.ToString();` and BTW `Word.Document oTgtDoc = new Word.Document();` won't return any new Word document (i.e. a 'real' new document in Word UI) but just a new word document object in your class."** Sure this is okay, in my code, just saved space. – ib11 May 13 '16 at 07:17
  • This one should return the page number count. Does it? `var PgNum = oTgtDoc.BuiltInDocumentProperties(Word.WdBuiltInProperty.wdPropertyPages).Value.ToString();` I don't think so. – ib11 May 13 '16 at 07:18
  • 1
    in my test it did. since I don't see your complete code maybe there's something missing and preventing it from functioning properly. see edited answer to show you how it worked for me – user3598756 May 13 '16 at 07:30
  • In fact, in the meantime I figured out how to exploit the Reflection. And I implemented that code, I even posted the code: http://stackoverflow.com/a/37202933/6201755 BUT your solution with `var PgNum = oTgtDoc.BuiltInDocumentProperties(Word.WdBuiltInProperty.wdPropertyPages).Value.‌​ToString();` is the best! More direct and faster. Except I need to use `Int.Parse` to cast it to `int` so I can do a comparison. – ib11 May 14 '16 at 04:24
  • glad to be of help. if my answer fulfilled your question please mark it as accepted. thank you – user3598756 May 14 '16 at 05:27
  • I am at a really tight spot. Both yours and Arina's are really good! You worked so much on this though but hers is the one I adopted as of now because of its succintness of code. I upvoted both of yours and hers and I am still testing. I think it is only fair if I accept the one that suits my application the best. But thank you very much for the lot of data and help in any case! – ib11 May 14 '16 at 05:50
  • you're welcome. anyhow as far as "succinctness" is concerned you may agree that one line is more succinct then two... finally I added `To.String()` for the sake of having a string type to put in a `Console.Writeline()` statement (though not even strictly needed). should you need an `int` you just remove that `To.String()` and thus the `Int.Parse()`, too. you could even use `int PgNum = ... ` instead of `var PgNum = ...`. see edited answer – user3598756 May 14 '16 at 06:15
  • So you say *Value* is an int? I did not test this. Now the only question is which is faster. Though that is not really crucial, since the amount of iteration thriugh the loop is minimal as I have a good starting value for the table column. Do you know which one is faster though to retrieve? The builtindocproperty or the statistics? – ib11 May 14 '16 at 06:33
  • `int PgNum = oTgtDoc.BuiltInDocumentProperties(Word.WdBuiltInProperty.wdPropertyPages).Value;` returns an `int`. `var PgNum = oTgtDoc.BuiltInDocumentProperties(Word.WdBuiltInProperty.wdPropertyPages).Value;` returns a `dynamic{int}`. I actually don't know which is faster since never had chance to test it, but that was not a issue of your original question which only asked for _"how to access this BuiltInDocumentProperties["Number of Pages"] from my C# code"_ – user3598756 May 14 '16 at 06:39
  • Sure. See answer of Arina. I meant that being faster or not. But you are right, this was not in the scope of the OP. – ib11 May 14 '16 at 06:44
  • well, your question was _"how to access this BuiltInDocumentProperties["Number of Pages"] "_ and not _"how to get a document number of Pages"_ , so I concentrated on a `"BuiltInDocumentProperties"` document property solution. – user3598756 May 14 '16 at 06:57
  • Awesome. You even solved that riddle +1 Lol. Okay, you won! And will you upvote my very well composed question? ;-) – ib11 May 14 '16 at 07:06
  • I'd agree this is a more complete answer to the question. And I am indeed a he. – Arina May 16 '16 at 01:46