Enable/disable context menu items

A

Alex

Hello,

I have the following problem:

My C# add-in adds several buttons to Word's context menus.
I need to enable/disable the buttons based on the cursor location.

Unfortunately, enabling/disabling several buttons on each of the about 50-60 context menus that I modified (with the associated interop costs) from the WindowSelectionChange event handler - potentially on every cursor move, does not seem like the optimal solution.

Is there a way to determine which context menu is about to pop up and only process that menu?

If not, are there any other speedup suggestions?

Thank you,
Alex.
 
P

Peter Huang [MSFT]

Hi

Based on my research, there is no such an event will tell us certain
context menu will be invoked.
Also I assume the context menu you mean should be the buildin one, and you
add more button into them.
So in the runtime, the context menu should be static.
e.g. if we right click on the document, it will pop up menu A, then we add
button to ,menu A, then in the following operation, the menu A should have
more buttons.
so I am somewhat confusion about why you need to do that in every mouse
click.

If I have any misunderstanding,please feel free to post here.

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
A

Alex

Hello Peter,

"Peter Huang" said:
Hi

Based on my research, there is no such an event will tell us certain
context menu will be invoked.
Also I assume the context menu you mean should be the buildin one, and you
add more button into them.
So in the runtime, the context menu should be static.
e.g. if we right click on the document, it will pop up menu A, then we add
button to ,menu A, then in the following operation, the menu A should have
more buttons.
so I am somewhat confusion about why you need to do that in every mouse
click.

If I have any misunderstanding,please feel free to post here.

Let me try again.

There is more than one context menu in Word.
Depending on what/where you right-click, you get a different context menu.

For example, if you right-click on an empty space you will get a different context menu than you'd get if you clicked on a misspelled word, or a picture, etc.

So, I need to add my button to *several* context menus.

Now, I am only interested in those menus that have "Copy" in them.
Enumerating all those, gives about 50 menus. VBA example:

Sub Test1()
On Error Resume Next
Dim s As String
Dim i As Integer
Dim bar As CommandBar
For Each bar In CommandBars
If bar.BuiltIn And (bar.Type = msoBarTypePopup) Then
i = 0
i = bar.Controls("Copy").id ' -- can trigger an error
If i > 0 Then
s = s & bar.Name & vbCrLf
End If
End If
Next
MsgBox s
End Sub

So, I have to add my button to about 50 menus.
That is fine, except that I now have to enable/disable it 50 times, potentially very often (I use the WindowSelectionChange event)
This sounds expensive, especially since it involves COM interop calls for each button.

Now, if I could know in advance which menu is about to pop up, I would only handle one button.

Otherwise, is there any other way to speed it up?


Best wishes,
Alex.
 
P

Peter Huang [MSFT]

Hi

Thanks for your quickly reply!
I understand your concern, based on my research word OM did not expose such
interface for us to get to know which context menu is about to pop up.
Because the menus only need to be added once into about 50 menus, so it is
once work.
But why you need to disable/enable them frequently?
e.g. when you right click on the document,, menu A will popup, then when
you will disable the added menu, and when enable them, what is the
condition?
I think adjust the enable/disable strategy may help to improve the
performance.

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
A

Alex

Hello Peter,

"Peter Huang" said:
Because the menus only need to be added once into about 50 menus, so it is once work.
But why you need to disable/enable them frequently?
e.g. when you right click on the document,, menu A will popup, then when
you will disable the added menu, and when enable them, what is the condition?
I think adjust the enable/disable strategy may help to improve the performance.

* Here's one example:

Let's say I want a context-menu item that will delete a bookmark.
I add this button to several context menus.

Obviously, I want it to be available only when the caret is positioned on a bookmark.
Otherwise, I want to either disable it or remove it altogether.
Multiply by ~50.


* Another example:

Select something in a Word document.
Right-click on it.
Notice the "Cut" and "Copy" items on the context menu.

Now, right-click outside the selection.
Notice the "Cut" and "Copy" items are still on the context menu but are now disabled.

Word does this dynamic update for all ~50 context menus.
I want to do the same for my buttons that I added to the context menus.


Best wishes,
Alex.
 
A

Alex

And the problems just keep multiplying...

I tried saving references to the buttons in an ArrayList and then setting the .Enabled property of the references.
This resulted in "Exception from HRESULT: 0x800A01A8".

I searched the internet for solutions and it seems that a similar question was asked before in this group:
http://tinyurl.com/92js8

I *really* need to solve this problem, please help!

Best wishes,
Alex.
 
P

Peter Huang [MSFT]

Hi

I think Word has it internal implement when design, but it did not expose
the interface for us to control our own button.
For your scenario, it seems that every time popup, the Button will be
recreated, so we need to retie onto the control.
[Create]
cbbb =
(CommandBarButton)wdApp.CommandBars[79].Controls.Add(MsoControlType.msoContr
olButton,oMissing,oMissing,oMissing,false);
cbbb.Caption = "1234";
cbbb.Enabled = false;
cbbb.Tag = "QWER";


[Change code]
CommandBarButton tcbb =(CommandBarButton
)wdApp.CommandBars.FindControl(oMissing,oMissing,"QWER",oMissing);//find
tcbb.Enabled = !tcbb.Enabled ;//set

So you may try to set the Tag in the hashtable and then call find every
time.
Hope this hellps.

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
A

Alex

Hello Peter,

"Peter Huang" said:
So you may try to set the Tag in the hashtable and then call find every time.
Hope this hellps.

That is over 50 COM-interop calls (since the tags have to be unique)

The reason that I posted the question is that I wanted to speed it up.


Best wishes,
Alex.
 
A

Alex

Update:

"Peter Huang" said:
So you may try to set the Tag in the hashtable and then call find every time.
Hope this hellps.

That is over 50 COM-interop calls (since the tags have to be unique).

I tried implementing it this way:

I kept a hashtable that maps the buttons' caption (same for all context menus) to an arraylist of the button buttons' tag (unique).
Then:

private void EventWindowBeforeRightClick(Selection selection, ref bool cancel)
{
try
{
object type = MsoControlType.msoControlButton;
bool enable = (something != null);

// Enable/disable some buttons
foreach (string tag in (IEnumerable) hash[caption1])
app.CommandBars.FindControl(type, missing, tag, missing).Enabled = enable;

// Enable/disable "Copy" buttons
foreach (string tag in (IEnumerable) hash[caption2])
app.CommandBars.FindControl(type, missing, tag, missing).Enabled = enable;

template.Saved = true;
}

catch (Exception ex)
{
// ...
}
}


This took about 15 seconds to process!
Totally unreasonable from a UI perspective.
 
P

Peter Huang [MSFT]

Hi

Thanks for your quickly reply!
I think the behavior that we need to use FindControl to get the created
button before is by design.
I know that will cause performance hit.
Based on my research so far, I did not find a way to speed that. Because
there is no such a interface in Word's OM to help to tell us which context
menu is about to popup.
Also I think you may also try to compose the disable/enable as a macro
which is buildin in the Word's instance which may save some efforts.
We can use the Application.Run in C# to run the VBA macro.

Also I suggest you contact MSPSS which is more suitable to handle the
Performance related problem.
You may reach MSPSS via the link below.
http://support.microsoft.com

Thanks for your understanding!

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
P

Peter Huang [MSFT]

Hi

Thanks for your quickly reply!
I think the behavior that we need to use FindControl to get the created
button before is by design.
I know that will cause performance hit.
Based on my research so far, I did not find a way to speed that. Because
there is no such a interface in Word's OM to help to tell us which context
menu is about to popup.
Also I think you may also try to compose the disable/enable as a macro
which is buildin in the Word's instance which may save some efforts.
We can use the Application.Run in C# to run the VBA macro.

Also I suggest you contact MSPSS which is more suitable to handle the
Performance related problem.
You may reach MSPSS via the link below.
http://support.microsoft.com

Thanks for your understanding!

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
A

Alex

Hello Peter,

"Peter Huang" said:
Also I think you may also try to compose the disable/enable as a macro
which is buildin in the Word's instance which may save some efforts.
We can use the Application.Run in C# to run the VBA macro.

That's an idea. Thanks.

Can you tell me how to pass a C# ArrayList to a VBA macro?


Best wishes,
Alex.
 
A

Alex

Hello Peter,

"Peter Huang" said:
Also I think you may also try to compose the disable/enable as a macro
which is buildin in the Word's instance which may save some efforts.
We can use the Application.Run in C# to run the VBA macro.

I cannot get it working.


I have a VBA macro in a template that I am loading:

// Load our private template as an add-in
object yes = true;
object path = "path to my template";
app.AddIns.Add((string) path, ref yes);


Then, in the template, I have the following in the project pane:

MyProject (My_Project)
Modules
HelperMacros


And the actual macro is:

Option Explicit

Public Sub EnableButtons(name As String, enable As Boolean)
Dim length As Integer
Dim ctl As CommandBarControl
Dim ctls As CommandBarControls

length = Len(name)
Set ctls = CommandBars.FindControls(Type:=msoControlButton)
For Each ctl In ctls
If Left(ctl.Tag, length) > 0 Then
ctl.Enabled = enable
End If
Next
End Sub


Then, I call it as follows:

try
{
string macro = "My_Project.HelperMacros.EnableButtons";
// also tried: "MyProject.HelperMacros.EnableButtons"
object name = "tag prefix";
object enable = true;

app.Run(macro, ref name, ref enable,
ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing);
}

catch (Exception ex)
{
// display exception
}


I am getting a "member not found" exception.


Best wishes,
Alex.
 
P

Peter Huang [MSFT]

Hi

Here is a KB about how to pass paramater from C# addin to VBA macro.
HOW TO: Run Office Macros by Using Automation from Visual C# .NET
http://support.microsoft.com/?kbid=306683

If you wants to pass the arraylist, we just need to change the code in the
kb above as below.
[VBA]
Public Sub DoKbTestWithParameter(sMsg As Variant)
MsgBox sMsg(0)
MsgBox sMsg(1)
End Sub

[C#]
ArrayList al = new ArrayList();
al.Add("Hello");
al.Add("World");
RunMacro(wdApp, new Object[]{"DoKbTestWithParameter",al});



Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
P

Peter Huang [MSFT]

Hi

I have replied to you in another thread in this queue.
Subject: Re: Enable/disable context menu items - URGENT
Newsgroups: microsoft.public.office.developer.com.add_ins

You may go and take a look.

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
A

Alex

Hello Peter,

"Peter Huang" said:
Hi

Here is a KB about how to pass paramater from C# addin to VBA macro.
HOW TO: Run Office Macros by Using Automation from Visual C# .NET
http://support.microsoft.com/?kbid=306683

If you wants to pass the arraylist, we just need to change the code in the
kb above as below.
[VBA]
Public Sub DoKbTestWithParameter(sMsg As Variant)
MsgBox sMsg(0)
MsgBox sMsg(1)
End Sub

[C#]
ArrayList al = new ArrayList();
al.Add("Hello");
al.Add("World");
RunMacro(wdApp, new Object[]{"DoKbTestWithParameter",al});

Thanks, but the signature of Application.RunMacro in Word XP PIA is different (takes 30 arguments).

Best wishes,
Alex.
 
A

Alex

Alex said:
string macro = "My_Project.HelperMacros.EnableButtons";
// also tried: "MyProject.HelperMacros.EnableButtons"

Got it working with just the name of the macro. That is:

string macro = "EnableButtons";

Best wishes,
Alex.
 
P

Peter Huang [MSFT]

Hi

I think you may try to take a look at the KB below
HOW TO: Run Office Macros by Using Automation from Visual C# .NET
http://support.microsoft.com/?kbid=306683

The RunMacro is a customized method defined in the KB above.

And I seems that you have figured that out, if you still have any concern,
please feel free to post here.

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
A

Alex

Hello Peter,

"Peter Huang" said:
I think you may try to take a look at the KB below

The RunMacro is a customized method defined in the KB above.

And I seems that you have figured that out, if you still have any concern,
please feel free to post here.

Yes, things seem to be working fine so far.

Thank you for your help.


Best wishes,
Alex.
 

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