Dynamic context menu for shape

T

Thomas Klaus

I know this item has been discussed several times, but couldn't find an
appropriate solution.

I've created a Visio Add-in in C#. Now i want to create a dynamic
context menu for a shape. That means, there exist several different
types of shapes and for each type, a different context menu is shown.

Actually, i'm using the Actions section in the shapesheet of a shape to
create a dynamic context menu. The problem is, i've got diagrams with
several hundred of shapes. So if i want to change a context menu for a
specific type of shape, i need to recreate the Actions section in the
shapesheet of every changed shape. Since this operation is too
expensive, there must be another solution.

Then i tried using the UIObject (Visio) and also the CommandBar
(Office) approach. But with this, when a user directly right click on a
shape without selecting the shape first, i'm not able to update the
context menu before the menu is shown. I'm catching the
SelectionChanged event, but at this time, the context menu is already
displayed.

I know with Visio 2003, there is the possibility to catch Mouse events,
but i also need to support Visio 2002.

Is there an appropriate solution or workaround for this problem?

Any help would be greatly appreciated.

Thanks,
Thomas Klaus
 
V

Vidush

Hi.
If you want to assign different menus with different kind of shapes, the
better way to go is to assign each kind of shape with a unique layer. Each
layer will define a set of action rows. Use the page "CreateSelection" method
to get collections of layered shapes, add the appropriate action rows to each
shape in the collection. If you want to dynamically change the shapes action
section via event, capture the appropriate event and change the shapes action
section, the layer assigned will help you decide witch action rows to add to
witch shape, as described above. It's really not that expensive, and it will
make your application much more stable.

Btw. The CreateSelection method exists only in Visio 2003, in Visio 2002 you
must get the page shapes collection and check the layer of each one.

I hope this helps.
 
T

Thomas Klaus

Thanks for your reply, Vidush.

Isn't there another possibilty with the UIObject (Visio) or the
CommandBar (Office) approach? Because among other things i want to be
able to create cascaded menus.

There must be the chance to anyhow catch the event, that the user
directly right clicked a shape with Visio 2002? Or to refresh the menu
when it is already displayed? Or to 'simulate' another right click to
update the menu?

Thanks,
Thomas Klaus
 
V

Vidush

Hi,

Unfortunately, using the UIObject to accomplish your goal will also force
handling the mouse right click event.

There is a solution for your problem but I don’t recommend using it. From my
experience, the shape ShapeSheet sections should be well defined when the
shape is being right clicked, otherwise the application suffers.

In order to capture the mouse right click event you can take advantaged of
the user32.dll.

The following example is written in VBA, but can be easily converted to C#.
In C#, in order to import the user32.dll you should Add reference to the
'System.Runtime.InteropServices' namespace, and use the DllImport attribute.

In VBA:
Create a new module and paste the following code:

Option Explicit

'declares
Public Declare Function SetWindowsHookEx Lib "user32" Alias
"SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As
Long, ByVal dwThreadId As Long) As Long

' In C# it will look like this:
' [DllImport("user32.dll")]
' protected static extern IntPtr SetWindowsHookEx(HookType idHook,
' HookProc lpfn,
' IntPtr hmod,
' int dwThreadId);

Public Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As
Long) As Long
Public Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long,
ByVal nCode As Long, ByVal wParam As Long, lParam As Any) As Long

'constant
Private Const WH_MOUSE_LL = 14&
Public Const HC_ACTION = 0&
Public Const WM_RBUTTONDOWN = &H204
Public Const WM_RBUTTONUP = &H205
Public Const VK_RBUTTON = &H2

Private lMShook As Long
Private bHookEnabled As Boolean

'functions which process mouse events
Public Function MouseProc(ByVal nCode As Long, ByVal wParam As Long, ByVal
lParam As Long) As Long
If nCode = HC_ACTION Then
If (wParam = WM_RBUTTONDOWN) Then
Debug.Print "Click"
Exit Function
End If
End If

MouseProc = CallNextHookEx(lMShook, nCode, wParam, lParam)
End Function

Public Sub SetHook()
If lMShook = 0 Then
lMShook = SetWindowsHookEx(WH_MOUSE_LL, AddressOf MouseProc, _
Application.InstanceHandle32, 0&)
End If
If lMShook = 0 Then
MsgBox "failed to install hook :" & lMShook & " : " & WH_MOUSE_LL
bHookEnabled = False

Exit Sub
Else
bHookEnabled = True
End If
End Sub

Public Sub UnSetHook()
If bHookEnabled Then
Call UnhookWindowsHookEx(lMShook)
lMShook = 0
End If
bHookEnabled = False

End Sub

I Hope any of this helps.
Good luck!
 
T

Thomas Klaus

Hi,

Thanks for your reply Vidush, you really helped me and finally i get
it to work!

The hardest part was to convert the screen position to the exact visio
coordinates, to find out, which shape would be selected. I had to use
the GetWindowRect function from the user32.dll to calculate the right
position. I don't know if i've done this too complicated, but couldn't
find a better solution. It was also depending, if the Rulers in Visio
was enabled or not. Is there probably a simpler solution for that?

--snip
// x-position of right click
winX = ...
// y-position of right click
winY = ...

// Get the window coordinates in Visio units.
VisioApplication.Instance.ActiveWindow.GetViewRect(out visioLeft, out
visioTop, out visioWidth, out visioHeight);

// Get the window coordinates in pixels.
VisioApplication.Instance.ActiveWindow.GetWindowRect(out pixelLeft,
out pixelTop, out pixelWidth, out pixelHeight);

// get correct left and top coordinates according the screen
coordinates
RECT r = new RECT();
GetWindowRect(VisioApplication.Instance.ActiveWindow.WindowHandle32,
ref r);

pixelWidth -= 16; // without scrollbar
pixelHeight -= 16; // without scrollbar
pixelLeft = r.left + 2;
pixelTop = r.top + 2;

if (VisioApplication.Instance.ActiveWindow.ShowRulers != 0)
{
// rulers are shown
pixelLeft += 16;
pixelWidth -= 16;
pixelTop += 16;
pixelHeight -= 16;
}

visioX = (double)(((winX -
pixelLeft)*(visioWidth/pixelWidth))+visioLeft);
visioY = (double)(visioTop - ((winY -
pixelTop)*(visioHeight/pixelHeight)));
--snip

Thanks,
Thomas Klaus
 
Y

Yevgeny Barkovsky

Sorry for primitive question, but if I want to run add-in function then
user-defined menu item clicked, how can I do that?
 

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