Office Commandbar Event not triggered and Word Normal.dot keeps growing

D

DYang

Hi

I am developing an Automation program in C# for Word XP. The program is running on Windows 2000 SP4 with Word XP SP3

Question 1: The CommandBarButton.Click event is not triggered for the first few items, but the rest works fine. What's wrong with my code
Question 2: The Normal.dot file grows around 9K bytes every time the program is executed. By opening Normal.dot, a section that has "Custom Popup ..." keeps increasing. All items in the command bar are removed before Word is closed and all items are added to the command bar with Temporary parameter set to true. After a while the file size of Normal.dot increases to around 3MB and keep growing, which makes opening and closing MS Word very slow. Is there anything I can do to stop Normal.dot from growing

Thanks a lot

DYan

Below is the sections of my program that adds and removes commmand bar
...
/// <summary
/// Create Office command bar items, designed to have all merge fields (@codes) from CU hos
/// </summary
/// <param name="MergeFields">DataView of all merge fields, sorted</param
/// <param name="cbrFields">Reference of the top level command bar for merge fields</param
/// <param name="CreateBarOnly">If true, only popup will be added; otherwise, the command bar buttons will be added
/// This parameter is essential to have popups appears at the top of the list and buttons follows all popup
/// Because the DataView is sorted by all columns and empty strings are sorted before non-empty ones
/// </param
private void AddCommandBarButtons(DataView MergeFields, ref Office.CommandBar cbrFields, bool CreateBarOnly

StringBuilder preCategory = new StringBuilder("preCat"); //intialize StringBuilder with non-empty strin
StringBuilder preSubCat1 = new StringBuilder("preSub1"); // so that Remove method will not generate exceptio
StringBuilder preSubCat2 = new StringBuilder("preSub2"); // the first time it is calle
StringBuilder category = new StringBuilder("category")
StringBuilder subCat1 = new StringBuilder("subCat1")
StringBuilder subCat2 = new StringBuilder("subCat2")
StringBuilder code = new StringBuilder("code")
StringBuilder name = new StringBuilder("name")
Office.CommandBarPopup cbrPopupParent = null
Office.CommandBarPopup cbrCurrentCategory = null, cbrCurrentCat1 = null, cbrCurrentCat2 = null

foreach (DataRowView row in MergeFields

tr

category.Remove(0, category.Length); //Clear the value in StringBuilde
subCat1.Remove(0, subCat1.Length)
subCat2.Remove(0, subCat2.Length)
code.Remove(0, code.Length)
name.Remove(0, name.Length)
if (row["Category"].ToString().Trim() == "") //set the value from DataView to StringBuilde
category.Append("[Not Categorized]")
els
category.Append(row["Category"].ToString())
subCat1.Append(row["SubCat1"].ToString())
subCat2.Append(row["SubCat2"].ToString())
code.Append(row["Code"].ToString())
name.Append(row["Name"].ToString())

// Add category if it does not exis
if (category.ToString() != preCategory.ToString()

cbrCurrentCategory = (Office.CommandBarPopup) cbrFields.FindControl(Office.MsoControlType.msoControlPopup, Optional, category.ToString(), true, true)
if (cbrCurrentCategory == null

cbrCurrentCategory = (Office.CommandBarPopup)cbrFields.Controls.Add
Office.MsoControlType.msoControlPopup, Optional, Optional, Optional, true)
cbrCurrentCategory.BeginGroup = true
cbrCurrentCategory.Caption = category.ToString()
cbrCurrentCategory.Tag = category.ToString()

preCategory.Remove(0, preCategory.Length)
preCategory.Append(category.ToString())
preSubCat1.Remove(0, preSubCat1.Length)
preSubCat1.Append("preSubCat1")
preSubCat2.Remove(0, preSubCat2.Length)
preSubCat2.Append("preSubCat2")

cbrPopupParent = cbrCurrentCategory
if (subCat1.Length > 0) //only check sub-categories when the string is not empt

if (subCat1.ToString() != preSubCat1.ToString()
{
cbrCurrentCat1 = (Office.CommandBarPopup)cbrPopupParent.CommandBar.FindControl(Office.MsoControlType.msoControlPopup, Optional, subCat1.ToString(), true, true);
if (cbrCurrentCat1 == null)
{
cbrCurrentCat1 = (Office.CommandBarPopup)cbrPopupParent.CommandBar.Controls.Add(
Office.MsoControlType.msoControlPopup, Optional, Optional, Optional, true);
cbrCurrentCat1.BeginGroup = true;
cbrCurrentCat1.Caption = subCat1.ToString();
cbrCurrentCat1.Tag = subCat1.ToString();
}
preSubCat1.Remove(0, preSubCat1.Length);
preSubCat1.Append(subCat1.ToString());
}
cbrPopupParent = cbrCurrentCat1;
if (subCat2.Length >0)
{
if (subCat2.ToString() != preSubCat2.ToString())
{
cbrCurrentCat2 = (Office.CommandBarPopup)cbrPopupParent.CommandBar.FindControl(Office.MsoControlType.msoControlPopup, Optional, subCat2.ToString(), true, true);
if (cbrCurrentCat2 == null)
{
cbrCurrentCat2 = (Office.CommandBarPopup)cbrPopupParent.CommandBar.Controls.Add(
Office.MsoControlType.msoControlPopup, Optional, Optional, Optional, true);
cbrCurrentCat2.BeginGroup = true;
cbrCurrentCat2.Caption = subCat2.ToString();
cbrCurrentCat2.Tag = subCat2.ToString();
}
preSubCat2.Remove(0, preSubCat2.Length);
preSubCat2.Append(subCat2.ToString());
}
cbrPopupParent = cbrCurrentCat2;
}
}
if (!CreateBarOnly)
{
Office.CommandBarButton popFields = (Office.CommandBarButton) cbrPopupParent.CommandBar.Controls.Add(
Office.MsoControlType.msoControlButton, Optional, Optional, Optional, true);
if (name.Length>0)
{
popFields.Caption = name.ToString();
popFields.Tag = name.ToString();
}
else
{
popFields.Caption = code.ToString();
popFields.Tag = code.ToString();
}

progressForm.Increment();
}
}
catch (Exception e)
{
CUWordErrorHandler.HandleError(e, this, "AddMergeFieldsCommandBar", "");
}
} //end of foreach loop
} // end of AddCommandBarButtons
....

/// <summary>
/// Subroutine to assign button click delegate to the Click event of the buttons on command bar
/// </summary>
/// <param name="fields">DtatView of the data source file that contains all merge fields</param>
/// <param name="cbrFields">object of the command bar</param>
private void AddCommandButtonClickEvent(DataView fields, Office.CommandBar cbrFields)
{
#if DEBUG
int i=0;
#endif

foreach(DataRowView row in fields)
{
try
{
Office.CommandBarButton btn = (Office.CommandBarButton) cbrFields.FindControl(Office.MsoControlType.msoControlButton, Optional, row["Name"].ToString(), true, true);
if (btn != null)
{
btn.Click += new Office._CommandBarButtonEvents_ClickEventHandler(cbrPopupButtonControl_Click);
#if DEBUG
i++;
#endif
}
}
catch (Exception e)
{
CUWordErrorHandler.HandleError(e, this, "AddMergeFieldsCommandBar", e.Message);
}
}
#if DEBUG
MessageBox.Show(i.ToString());
#endif
}
.....

/// <summary>
/// Remove command bar created
/// </summary>
public void RemoveCommandBar()
{
try
{ //remove each every controls on commandbar that created, otherwise the size of Normal.dot
//will increase every time the template createion is called
//deleting the root command bar will not do the trick as for Word XP
Office.CommandBar cbrControl = oApp.CommandBars["TCS Merge Fields"];
string[] fields = new string[500];
int count=0;
if (cbrControl != null)
{
foreach(Office.CommandBarPopup popup in cbrControl.Controls)
{
foreach(Office.CommandBarControl popup2 in popup.Controls)
{
if (popup2.Type == Office.MsoControlType.msoControlPopup)
{
Office.CommandBarPopup tmp = (Office.CommandBarPopup) popup2;
foreach(Office.CommandBarControl ctrl in tmp.Controls)
{
Office.CommandBarButton tmp1 = (Office.CommandBarButton) ctrl;
fields[count++]=ctrl.Tag;
tmp1.Delete(false);
}
tmp=null;
}
fields[count++]=popup2.Tag;
popup2.Delete(false);
}
fields[count++]=popup.Tag;
popup.Delete(false);
}
cbrControl.Delete();
cbrControl = null;
StreamWriter wr = File.CreateText(@"c:\fields.txt");
for (int i=0;i<count;i++)
wr.WriteLine(fields);
wr.Flush();
wr.Close();
}
}
catch(Exception e)
{
CUWordErrorHandler.HandleError(e, this, "RemoveCommandBar", "Name = Collector System Merge Fields");
}
}
 
M

Mark Bower [MSFT]

Item 1. You need to make you button objects class-level variables in order
to catch the click event reliably. Otherwise some indeterminate time after
you exit your AddCommandButtonClickEvent function the garbage collector will
clean up your btn objects which are currently scoped at the method level,
including releasing your event hooks.
Item 2. My understanding is that unlike the other office apps Word ignores
the Temporary parameter. Growth of the Normal.dot is most likely due to
your work with the menus, although other stuff can get added there as well.
Best approach is probably to check if the command bar buttons already exist
at start-up and create them only if they don't already exist.

--
Mark Bower
Microsoft

This post is provided 'as-is' without warranty and confers no rights.

Hi,

I am developing an Automation program in C# for Word XP. The program is
running on Windows 2000 SP4 with Word XP SP3.
Question 1: The CommandBarButton.Click event is not triggered for the
first few items, but the rest works fine. What's wrong with my code?
Question 2: The Normal.dot file grows around 9K bytes every time the
program is executed. By opening Normal.dot, a section that has "Custom Popup
...." keeps increasing. All items in the command bar are removed before Word
is closed and all items are added to the command bar with Temporary
parameter set to true. After a while the file size of Normal.dot increases
to around 3MB and keep growing, which makes opening and closing MS Word very
slow. Is there anything I can do to stop Normal.dot from growing?
Thanks a lot.

DYang

Below is the sections of my program that adds and removes commmand bar.
...
/// <summary>
/// Create Office command bar items, designed to have all merge fields (@codes) from CU host
/// </summary>
/// <param name="MergeFields">DataView of all merge fields, sorted</param>
/// <param name="cbrFields">Reference of the top level command bar for
merge fields said:
/// <param name="CreateBarOnly">If true, only popup will be added;
otherwise, the command bar buttons will be added.
/// This parameter is essential to have popups appears at the top of the
list and buttons follows all popup.
/// Because the DataView is sorted by all columns and empty strings are sorted before non-empty ones.
/// </param>
private void AddCommandBarButtons(DataView MergeFields, ref
Office.CommandBar cbrFields, bool CreateBarOnly)
{
StringBuilder preCategory = new StringBuilder("preCat"); //intialize
StringBuilder with non-empty string
StringBuilder preSubCat1 = new StringBuilder("preSub1"); // so that Remove
method will not generate exception
StringBuilder preSubCat2 = new StringBuilder("preSub2"); // the first time it is called
StringBuilder category = new StringBuilder("category");
StringBuilder subCat1 = new StringBuilder("subCat1");
StringBuilder subCat2 = new StringBuilder("subCat2");
StringBuilder code = new StringBuilder("code");
StringBuilder name = new StringBuilder("name");
Office.CommandBarPopup cbrPopupParent = null;
Office.CommandBarPopup cbrCurrentCategory = null, cbrCurrentCat1 = null, cbrCurrentCat2 = null;

foreach (DataRowView row in MergeFields)
{
try
{
category.Remove(0, category.Length); //Clear the value in StringBuilder
subCat1.Remove(0, subCat1.Length);
subCat2.Remove(0, subCat2.Length);
code.Remove(0, code.Length);
name.Remove(0, name.Length);
if (row["Category"].ToString().Trim() == "") //set the value from DataView to StringBuilder
category.Append("[Not Categorized]");
else
category.Append(row["Category"].ToString());
subCat1.Append(row["SubCat1"].ToString());
subCat2.Append(row["SubCat2"].ToString());
code.Append(row["Code"].ToString());
name.Append(row["Name"].ToString());

// Add category if it does not exist
if (category.ToString() != preCategory.ToString())
{
cbrCurrentCategory = (Office.CommandBarPopup)
cbrFields.FindControl(Office.MsoControlType.msoControlPopup, Optional,
category.ToString(), true, true);
if (cbrCurrentCategory == null)
{
cbrCurrentCategory = (Office.CommandBarPopup)cbrFields.Controls.Add(
Office.MsoControlType.msoControlPopup, Optional, Optional, Optional, true);
cbrCurrentCategory.BeginGroup = true;
cbrCurrentCategory.Caption = category.ToString();
cbrCurrentCategory.Tag = category.ToString();
}
preCategory.Remove(0, preCategory.Length);
preCategory.Append(category.ToString());
preSubCat1.Remove(0, preSubCat1.Length);
preSubCat1.Append("preSubCat1");
preSubCat2.Remove(0, preSubCat2.Length);
preSubCat2.Append("preSubCat2");
}
cbrPopupParent = cbrCurrentCategory;
if (subCat1.Length > 0) //only check sub-categories when the string is not empty
{
if (subCat1.ToString() != preSubCat1.ToString())
{
cbrCurrentCat1 =
(Office.CommandBarPopup)cbrPopupParent.CommandBar.FindControl(Office.MsoCont
rolType.msoControlPopup, Optional, subCat1.ToString(), true, true);
if (cbrCurrentCat1 == null)
{
cbrCurrentCat1 = (Office.CommandBarPopup)cbrPopupParent.CommandBar.Controls.Add(
Office.MsoControlType.msoControlPopup, Optional, Optional, Optional, true);
cbrCurrentCat1.BeginGroup = true;
cbrCurrentCat1.Caption = subCat1.ToString();
cbrCurrentCat1.Tag = subCat1.ToString();
}
preSubCat1.Remove(0, preSubCat1.Length);
preSubCat1.Append(subCat1.ToString());
}
cbrPopupParent = cbrCurrentCat1;
if (subCat2.Length >0)
{
if (subCat2.ToString() != preSubCat2.ToString())
{
cbrCurrentCat2 =
(Office.CommandBarPopup)cbrPopupParent.CommandBar.FindControl(Office.MsoCont
rolType.msoControlPopup, Optional, subCat2.ToString(), true, true);
if (cbrCurrentCat2 == null)
{
cbrCurrentCat2 = (Office.CommandBarPopup)cbrPopupParent.CommandBar.Controls.Add(
Office.MsoControlType.msoControlPopup, Optional, Optional, Optional, true);
cbrCurrentCat2.BeginGroup = true;
cbrCurrentCat2.Caption = subCat2.ToString();
cbrCurrentCat2.Tag = subCat2.ToString();
}
preSubCat2.Remove(0, preSubCat2.Length);
preSubCat2.Append(subCat2.ToString());
}
cbrPopupParent = cbrCurrentCat2;
}
}
if (!CreateBarOnly)
{
Office.CommandBarButton popFields = (Office.CommandBarButton) cbrPopupParent.CommandBar.Controls.Add(
Office.MsoControlType.msoControlButton, Optional, Optional, Optional, true);
if (name.Length>0)
{
popFields.Caption = name.ToString();
popFields.Tag = name.ToString();
}
else
{
popFields.Caption = code.ToString();
popFields.Tag = code.ToString();
}

progressForm.Increment();
}
}
catch (Exception e)
{
CUWordErrorHandler.HandleError(e, this, "AddMergeFieldsCommandBar", "");
}
} //end of foreach loop
} // end of AddCommandBarButtons
...

/// <summary>
/// Subroutine to assign button click delegate to the Click event of the buttons on command bar
/// </summary>
/// <param name="fields">DtatView of the data source file that contains
all merge fields said:
/// <param name="cbrFields">object of the command bar</param>
private void AddCommandButtonClickEvent(DataView fields, Office.CommandBar cbrFields)
{
#if DEBUG
int i=0;
#endif

foreach(DataRowView row in fields)
{
try
{
Office.CommandBarButton btn = (Office.CommandBarButton)
cbrFields.FindControl(Office.MsoControlType.msoControlButton, Optional,
row["Name"].ToString(), true, true);
if (btn != null)
{
btn.Click += new Office._CommandBarButtonEvents_ClickEventHandler(cbrPopupButtonControl_Click
);
#if DEBUG
i++;
#endif
}
}
catch (Exception e)
{
CUWordErrorHandler.HandleError(e, this, "AddMergeFieldsCommandBar", e.Message);
}
}
#if DEBUG
MessageBox.Show(i.ToString());
#endif
}
....

/// <summary>
/// Remove command bar created
/// </summary>
public void RemoveCommandBar()
{
try
{ //remove each every controls on commandbar that created, otherwise the size of Normal.dot
//will increase every time the template createion is called
//deleting the root command bar will not do the trick as for Word XP
Office.CommandBar cbrControl = oApp.CommandBars["TCS Merge Fields"];
string[] fields = new string[500];
int count=0;
if (cbrControl != null)
{
foreach(Office.CommandBarPopup popup in cbrControl.Controls)
{
foreach(Office.CommandBarControl popup2 in popup.Controls)
{
if (popup2.Type == Office.MsoControlType.msoControlPopup)
{
Office.CommandBarPopup tmp = (Office.CommandBarPopup) popup2;
foreach(Office.CommandBarControl ctrl in tmp.Controls)
{
Office.CommandBarButton tmp1 = (Office.CommandBarButton) ctrl;
fields[count++]=ctrl.Tag;
tmp1.Delete(false);
}
tmp=null;
}
fields[count++]=popup2.Tag;
popup2.Delete(false);
}
fields[count++]=popup.Tag;
popup.Delete(false);
}
cbrControl.Delete();
cbrControl = null;
StreamWriter wr = File.CreateText(@"c:\fields.txt");
for (int i=0;i<count;i++)
wr.WriteLine(fields);
wr.Flush();
wr.Close();
}
}
catch(Exception e)
{
CUWordErrorHandler.HandleError(e, this, "RemoveCommandBar", "Name =

Collector System Merge Fields");
 
C

Cindy M -WordMVP-

Hi =?Utf-8?B?RFlhbmdAbmV3c2dyb3VwLm5vc3BhbQ==?=,

In addition to what Mark Bower has replied to you: when you add and delete controls
from the command bars do you explicitly declare a CustomizationContext just before
these lines of code? If you do not, you should. Word won't necessarily create and
delete from the same document container if you don't.
Question 2: The Normal.dot file grows around 9K bytes every time the program is
executed. By opening Normal.dot, a section that has "Custom Popup ..." keeps
increasing. All items in the command bar are removed before Word is closed and all
items are added to the command bar with Temporary parameter set to true. After a while
the file size of Normal.dot increases to around 3MB and keep growing, which makes
opening and closing MS Word very slow. Is there anything I can do to stop Normal.dot
from growing?
Cindy Meister
INTER-Solutions, Switzerland
http://homepage.swissonline.ch/cindymeister (last update Sep 30 2003)
http://www.word.mvps.org

This reply is posted in the Newsgroup; please post any follow question or reply in the
newsgroup and not by e-mail :)
 
D

DYang

Thanks for the replies

Mark
I have one class level variable for all the Commandbar buttons, but it does not resolve the problem. Do I need to have different variable names for each individual commandbar button? (I hope not
Currently, there are 272 command bar buttons. If the number of commandbar buttons is limited to 250, the click event for all buttons triggers fine. However, I have loaded around 1800 buttons on the development machine without any problem. Would the amount of system memory play a role in this issue? Is there any limit of the total number of buttons one can put on a command bar

Cindy
Your suggestion works, but it only works when CustomizationContext is set to Application.ActiveDocument. The file size of Nomal.dot still grows when CustomizationContext is set to Application.NormalTemplate

Thanks agai
 
C

Cindy M -WordMVP-

Hi =?Utf-8?B?RFlhbmdAbmV3c2dyb3VwLm5vc3BhbQ==?=,
Your suggestion works, but it only works when CustomizationContext is set to
Application.ActiveDocument. The file size of Nomal.dot still grows when
CustomizationContext is set to Application.NormalTemplate.Why would you set the context to NormalTemplate, and then not leave the buttons?

Note: I don't disagree that your observation that Normal.dot size increasing is
odd. OTOH, what you're doing could be considered as "mishandling" Word :) Can you
describe in "plain English" what your Addin is meant for? Not just the spec, but
when/how it is to be used. Should it only be there for certain tasks? Present all
the time? Will the button names vary during a "session"?

As to the question of "how many buttons": Those who've programmed the Word/Office
interface a lot have run into max. limits for things like controls. Both in
CommandBars as well as in UserForms. I've never seen an official upper limit
documented, and the behavior isn't 100% predictable, but from 255 on up, both
interfaces definitely become unstable. (Note: not my personal experience, but I've
seen it mentioned a few times in the Word.VBA newsgroups.) 255 tends to be a
breaking point on a lot of things in Word...

Cindy Meister
INTER-Solutions, Switzerland
http://homepage.swissonline.ch/cindymeister (last update Sep 30 2003)
http://www.word.mvps.org

This reply is posted in the Newsgroup; please post any follow question or reply in
the newsgroup and not by e-mail :)
 
D

DYang

The application is created to generate Word templates and create letters from the templates created for a legacy database. It is a stand alone executable (an .exe), not an add-in for Word. The purpose of the command bar is to categorize and to display all the mail merge fields mapped in the legacy database. The mail merge fields could be changed, added, or deleted, therefore, the list is dynamic. The command bar only needs to be displayed when the user is creating Word templates. Therefore, during template creation, the command bar needs to be loaded when the task starts and be removed when it is done

I am trying to capture the click event of the command bar buttons; so that when the user clicks the command bar button a mail merge field will be inserted at the cursor position

The mixed behavior that I got from different machines bothers me. Since, this is a commercial product we need to make the behavior predictable or at least know where the limit is

You mentioned "mishandling" in previous email. I hope the above description is sufficient to direct me into the right direction. Please let me know if you need more information

Thanks a lo

P.S. English is not my mother tongue. Please excuse me if my sentences are difficult to understand. Any critics and directions are welcome, especially directions in this case. Please don't hesitate in providing any good advices.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top