0

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);
        }
    }
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Nick_F
  • 1,103
  • 2
  • 13
  • 30

2 Answers2

2

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.

Cindy Meister
  • 25,071
  • 21
  • 34
  • 43
  • Thank you Cindy. My first attempt was to use the Find function, but I didn't know how to do it. After some more digging on the net and even some personal thinking, I was able to come up with a code that meets my needs. – Nick_F Dec 14 '15 at 02:00
1

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;
    }
Nick_F
  • 1,103
  • 2
  • 13
  • 30
  • Great :-) Yes, bookmark names in a document must be unique. One remark on your code: You don't need to SELECT the Range in order to bookmark it. Generally, "we" wouldn't do that - especially not when a series of actions is being performed - as it causes screen flicker (annoying for the user) and does slow down processing. Try commenting out rngStory.Select(); and see if your code executes satisfactorily without? If you want to leave the user "at" the last bookmark I'd do a select at the end of your code. – Cindy Meister Dec 14 '15 at 12:28
  • Thanks for advice Cindy. It works better without Select(). – Nick_F Dec 14 '15 at 12:40