Nick_F
Nick_F

Reputation: 1117

Word Interop Add a bookmark to a header

I have a .doc Word document and there is a text in header. I want to find the word "MyWord" from the header and add a bookmark to it (bookmark is also called "MyWord"). At the moment the code I have is able to search in headers and footers, but I don't know how to select the word of interest. I used a string variable to load the text content of the header and I find the start and end of my word. However, when I select this range, the selection highlights a different area. Here is the code:

    public static void AddBookmarkAnywhere(Microsoft.Office.Interop.Word.Application app, string findText, string bookmarkName)
    {
        var doc = app.ActiveDocument;
        foreach (Microsoft.Office.Interop.Word.Range rngStory in doc.StoryRanges)
        {
            var internalRangeStory = rngStory;
            do
            {
                AddBookmarkInStory(internalRangeStory, findText, bookmarkName);
                try
                {
                    switch (internalRangeStory.StoryType)
                    {
                        case Microsoft.Office.Interop.Word.WdStoryType.wdEvenPagesHeaderStory: // 6
                        case Microsoft.Office.Interop.Word.WdStoryType.wdPrimaryHeaderStory:   // 7
                        case Microsoft.Office.Interop.Word.WdStoryType.wdEvenPagesFooterStory: // 8
                        case Microsoft.Office.Interop.Word.WdStoryType.wdPrimaryFooterStory:   // 9
                        case Microsoft.Office.Interop.Word.WdStoryType.wdFirstPageHeaderStory: // 10
                        case Microsoft.Office.Interop.Word.WdStoryType.wdFirstPageFooterStory: // 11

                        if (internalRangeStory.ShapeRange.Count > 0)
                        {
                            foreach (Microsoft.Office.Interop.Word.Shape oShp in internalRangeStory.ShapeRange)
                            {
                                if (oShp.TextFrame.HasText != 0)
                                {
                                    AddBookmarkInStory(oShp.TextFrame.TextRange, findText, bookmarkName);
                                }
                            }
                        }
                        break;

                        default:
                            break;
                    }
                }
                catch
                {
                    MessageBox.Show("Some error in function FindReplaceAnywhere");
                }

                internalRangeStory = internalRangeStory.NextStoryRange;
            }
            while (internalRangeStory != null); 
        }

    }

    private static void AddBookmarkInStory(Microsoft.Office.Interop.Word.Range rngStory, string strSearch, string strBookmarkName)
    {
        string text = rngStory.Text;
        int start = text.IndexOf(strSearch);
        int end = start + strSearch.Length;

        if(start >= 0)
        {
            rngStory.Start = start; // incorrect value
            rngStory.End = start + strSearch.Length;
            rngStory.Select();
            rngStory.Bookmarks.Add(strBookmarkName, rngStory);
        }
    }

Upvotes: 0

Views: 1804

Answers (2)

Nick_F
Nick_F

Reputation: 1117

I found a way to solve my problem. In my case, each Range has only ONE unique bookmark to be added. If the header on first page has a word "MyWord" where I want to place a bookmark, the code below will place "MyWord1". If the header on another page has the same word, "MyWord", a bookmark named "MyWord2" will be placed.

    public static void AddBookmarkAnywhere(Microsoft.Office.Interop.Word.Application app, string findText, string bookmarkName)
    {
        var doc = app.ActiveDocument;
        bool bFound;
        int occurenceNumber = 1; 
        foreach (Microsoft.Office.Interop.Word.Range rngStory in doc.StoryRanges)
        {
            var internalRangeStory = rngStory;
            do
            {
                bFound = AddBookmarkInStory(internalRangeStory, findText, bookmarkName + occurenceNumber.ToString());
                if(bFound)
                {
                    occurenceNumber++;
                }

                try
                {
                    switch (internalRangeStory.StoryType)
                    {
                        case Microsoft.Office.Interop.Word.WdStoryType.wdEvenPagesHeaderStory: // 6
                        case Microsoft.Office.Interop.Word.WdStoryType.wdPrimaryHeaderStory:   // 7
                        case Microsoft.Office.Interop.Word.WdStoryType.wdEvenPagesFooterStory: // 8
                        case Microsoft.Office.Interop.Word.WdStoryType.wdPrimaryFooterStory:   // 9
                        case Microsoft.Office.Interop.Word.WdStoryType.wdFirstPageHeaderStory: // 10
                        case Microsoft.Office.Interop.Word.WdStoryType.wdFirstPageFooterStory: // 11

                        if (internalRangeStory.ShapeRange.Count > 0)
                        {
                            foreach (Microsoft.Office.Interop.Word.Shape oShp in internalRangeStory.ShapeRange)
                            {
                                if (oShp.TextFrame.HasText != 0)
                                {
                                    AddBookmarkInStory(oShp.TextFrame.TextRange, findText, bookmarkName);
                                }
                            }
                        }
                        break;

                        default:
                            break;
                    }
                }
                catch
                {
                    MessageBox.Show("Some error in function AddBookmarkAnywhere");
                }

                internalRangeStory = internalRangeStory.NextStoryRange;
            }
            while (internalRangeStory != null); 
        }
    }

    private static bool AddBookmarkInStory(Microsoft.Office.Interop.Word.Range rngStory, string strSearch, string bookmarkName)
    {
        rngStory.Find.ClearFormatting();
        rngStory.Find.Replacement.ClearFormatting();
        rngStory.Find.Text = strSearch;
        rngStory.Find.Replacement.Text = string.Empty;

        object findText = strSearch;
        object replaceText = strSearch;
        object matchCase = true;
        object matchWholeWord = true;
        object matchWildCards = false;
        object matchSoundsLike = false;
        object matchAllWordForms = false;
        object forward = true;
        object format = false;
        object matchKashida = false;
        object matchDiacritics = false;
        object matchAlefHamza = false;
        object matchControl = false;
        object read_only = false;
        object visible = true;
        object replace = 0;  // replace = 0 replaces None, replace = 1 replaces One, replace = 2 replaces All
        object wrap = 1;

        string bookmarkStr;
        bool isFound = false;
        isFound = rngStory.Find.Execute(strSearch, matchCase, matchWholeWord,
            matchWildCards, matchSoundsLike, matchAllWordForms, forward, 
            wrap, format, replaceText, replace, matchKashida, 
            matchDiacritics, matchAlefHamza, matchControl);            

        if(isFound) {
            rngStory.Select();
            rngStory.Bookmarks.Add(bookmarkName, rngStory);
            return true;
        }

        return false;
    }

Upvotes: 1

Cindy Meister
Cindy Meister

Reputation: 25693

You have the right idea, you're just missing the FIND functionality, which you need to use in place of "IndexOf" and trying to capture the position of the text using the Start and End properties of the Range object.

These are not a reliable way to work with text in a Word document because Word stores so much information as "non-printing characters". Just as an example, if your Header is displaying a dynamic page number or a date there's a field code behind the scenes that's messing with the Start and End positions.

So look up FIND (property for the Selection and the Range object - you definitely want RANGE.FIND) in the language reference to get an feeling for it. In order to find out the right syntax, use it in the UI while recording a macro then look at the VBA and compare it to the language reference. There are also tons of sample code circulating in the Internet.

Important to note is that on a successful Find.Execute the RANGE object on which Find runs will contain the "found" instance of the search term. So you can simply use that as the "target" for Bookmarks.Add.

Upvotes: 2

Related Questions