Hi Linda,
Sorry for the delay - been a bit sidetracked with other projects...
Your code worked fantastically, so thanks for that. Converted the
implmentation to VB, so here it is in VB.Net for anyone else who may need
some help with this problem (outlook express put all the extra spacing in!)
David
Imports System.ComponentModel
Imports System.ComponentModel.Design
Public Class MenuCommandServiceImpl
Implements IMenuCommandService
' the host
Private host As IDesignerHost
' commandId-command mapping
Private commands As IDictionary
' menuItem-verb mapping
Private menuItemVerb As IDictionary
' the global verbs collection.
Private globalVerbs As DesignerVerbCollection
' we use the same context menu over-and-over
Private contextMenu As ContextMenu
' we keep the lastSelectedComponent around
Private lastSelectedComponent As IComponent
Public Sub New(ByVal host As IDesignerHost)
Me.host = host
commands = New Hashtable()
globalVerbs = New DesignerVerbCollection()
menuItemVerb = New Hashtable()
contextMenu = New ContextMenu()
lastSelectedComponent = Nothing
End Sub
#Region "Implementation of IMenuCommandService"
'/ called to add a MenuCommand
Public Sub AddCommand(ByVal command As
System.ComponentModel.Design.MenuCommand) Implements
IMenuCommandService.AddCommand
If command Is Nothing Then
Throw New ArgumentException("command")
End If
' don't add commands twice
If FindCommand(command.CommandID) Is Nothing Then
commands.Add(command.CommandID, command)
End If
End Sub
'/ called to remove a MenuCommand
Public Sub RemoveCommand(ByVal command As
System.ComponentModel.Design.MenuCommand) Implements
IMenuCommandService.RemoveCommand
If command Is Nothing Then
Throw New ArgumentException("command")
End If
commands.Remove(command.CommandID)
End Sub
'/ called when to add a global verb
Public Sub AddVerb(ByVal verb As System.ComponentModel.Design.DesignerVerb)
Implements IMenuCommandService.AddVerb
If verb Is Nothing Then
Throw New ArgumentException("verb")
End If
globalVerbs.Add(verb)
' create a menu item for the verb and add it to the context Menu
Dim menuItem As MenuItem = New MenuItem(verb.Text)
AddHandler menuItem.Click, AddressOf MenuItemClickHandler
menuItemVerb.Add(MenuItem, verb)
contextMenu.MenuItems.Add(MenuItem)
End Sub
'/ called to remove global verb
Public Sub RemoveVerb(ByVal verb As
System.ComponentModel.Design.DesignerVerb) Implements
IMenuCommandService.RemoveVerb
If verb Is Nothing Then
Throw New ArgumentException("verb")
End If
globalVerbs.Remove(verb)
' find the menu item associated with the verb
Dim associatedMenuItem As MenuItem = Nothing
Dim de As DictionaryEntry
For Each de In menuItemVerb
If de.Value Is verb Then
associatedMenuItem = de.Key
Exit For
End If
Next
' if we found the verb's menu item, remove it
If Not associatedMenuItem Is Nothing Then
menuItemVerb.Remove(associatedMenuItem)
End If
' remove the verb from the context menu too
contextMenu.MenuItems.Remove(associatedMenuItem)
End Sub
'/ returns the MenuCommand associated with the commandId.
Public Function FindCommand(ByVal commandID As CommandID) As MenuCommand
Implements IMenuCommandService.FindCommand
Return commands(commandID)
End Function
'/ called to invoke a command
Public Function GlobalInvoke(ByVal commandID As
System.ComponentModel.Design.CommandID) As Boolean Implements
IMenuCommandService.GlobalInvoke
Dim result As Boolean = False
Dim command As MenuCommand = FindCommand(commandID)
If Not command Is Nothing Then
command.Invoke()
result = True
End If
Return result
End Function
'/ called to show the context menu for the selected component.
Public Sub ShowContextMenu(ByVal menuID As
System.ComponentModel.Design.CommandID, ByVal x As Integer, ByVal y As
Integer) Implements IMenuCommandService.ShowContextMenu
Dim selectionService As ISelectionService =
host.GetService(GetType(ISelectionService))
' get the primary component
Dim primarySelection As IComponent = selectionService.PrimarySelection
' if the he clicked on the same component again then just show the(context)
' menu. otherwise, we have to throw away the previous
' set of local menu items and create new ones for the newly
' selected component
If lastSelectedComponent Is primarySelection Then
' remove all non-global menu items from the context menu
ResetContextMenu()
' get the designer
Dim designer As IDesigner = host.GetDesigner(primarySelection)
' not all controls need a desinger
If Not designer Is Nothing Then
' get designer's verbs
Dim verbs As DesignerVerbCollection = designer.Verbs
Dim verb As DesignerVerb
For Each verb In verbs
' add new menu items to the context menu
CreateAndAddLocalVerb(verb)
Next
End If
End If
' we only show designer context menus for controls
If TypeOf primarySelection Is Control Then
Dim comp As Control = primarySelection
Dim pt As Point = comp.PointToScreen(New Point(0, 0))
contextMenu.Show(comp, New Point(x - pt.X, y - pt.Y))
End If
' keep the selected component for next time
lastSelectedComponent = primarySelection
End Sub
'/ returns the the current designer verbs
Public ReadOnly Property Verbs() As
System.ComponentModel.Design.DesignerVerbCollection Implements
IMenuCommandService.Verbs
Get
' create a new collection
Dim availableVerbs As DesignerVerbCollection = New DesignerVerbCollection()
' add the global verbs
If globalVerbs Is Nothing Then
If globalVerbs.Count > 0 Then
availableVerbs.AddRange(globalVerbs)
End If
End If
' now add the local verbs
Dim selectionService As ISelectionService =
host.GetService(GetType(ISelectionService))
Dim primaryComponent As IComponent = selectionService.PrimarySelection
If Not primaryComponent Is Nothing Then
Dim designer As IDesigner = host.GetDesigner(primaryComponent)
If Not designer Is Nothing Then
If designer.Verbs Is Nothing Then
availableVerbs.AddRange(designer.Verbs)
End If
End If
End If
Return availableVerbs
End Get
End Property
#End Region
' called to invoke menu item verbs
Private Sub MenuItemClickHandler(ByVal sender As Object, ByVal e As
EventArgs)
' get the menu item
Dim menuItem As MenuItem = sender
If Not menuItem Is Nothing Then
' get and invoke the verb
Dim verb As DesignerVerb = menuItemVerb(menuItem)
If Not verb Is Nothing Then
Try
verb.Invoke()
Catch ex As Exception
' do nothing
End Try
End If
End If
End Sub
' removes all local verbs from the context menu
Private Sub ResetContextMenu()
If Not contextMenu Is Nothing Then
If contextMenu.MenuItems Is Nothing Then
Dim menuItemArray() As MenuItem = Nothing
contextMenu.MenuItems.CopyTo(menuItemArray, 0)
Dim menuItem As MenuItem
For Each menuItem In menuItemArray
' if its not in the global list, remove it
If Not IsInGlobalList(menuItem.Text) Then
contextMenu.MenuItems.Remove(menuItem)
End If
' get rid of the menu item from the mapping
menuItemVerb.Remove(menuItem)
Next
End If
End If
End Sub
' creats and adds a local verb
Private Sub CreateAndAddLocalVerb(ByVal verb As DesignerVerb)
If verb Is Nothing Then
Throw New ArgumentException("verb")
End If
VerifyVerb(verb)
' create a menu item for the verb
Dim menuItem As MenuItem = New MenuItem(verb.Text)
' attach the menu item click listener
AddHandler menuItem.Click, AddressOf MenuItemClickHandler
' do the menuItem-verb mapping
menuItemVerb.Add(MenuItem, verb)
' add to context menu
contextMenu.MenuItems.Add(MenuItem)
End Sub
' returns true if the verb is in the global verb collection
Private Function IsInGlobalList(ByVal verbText As String) As Boolean
Dim found As Boolean = False
If Not globalVerbs Is Nothing Then
If globalVerbs.Count > 0 Then
Dim dv As DesignerVerb
For Each dv In globalVerbs
If String.Compare(dv.Text, verbText, True) = 0 Then
found = True
Exit For
End If
Next
End If
End If
Return found
End Function
' we can't add the same verb twice
Private Sub VerifyVerb(ByVal verb As DesignerVerb)
If verb Is Nothing Then
Throw New ArgumentException("verb")
End If
' make sure the verb is not in the global list
If Not globalVerbs Is Nothing Then
If globalVerbs.Count > 0 Then
Dim dv As DesignerVerb
For Each dv In globalVerbs
If String.Compare(dv.Text, verb.Text, True) = 0 Then
Throw New Exception("Cannot add the same verb twice.")
End If
Next
End If
End If
' now check the menuItemVerb mapping
If Not menuItemVerb Is Nothing Then
If menuItemVerb.Count > 0 Then
Dim dv As DesignerVerb
For Each dv In menuItemVerb.Values
If String.Compare(dv.Text, verb.Text, True) = 0 Then
Throw New Exception("Cannot add the same verb twice.")
End If
Next
End If
End If
End Sub
End Class
'' add the MenuCommandService to the class HostSurface
Public Class HostSurface
Inherits DesignSurface
Public Sub New()
MyBase.New()
Dim host As IDesignerHost = Me.GetService(GetType(IDesignerHost))
host.AddService(GetType(IMenuCommandService), New
MenuCommandServiceImpl(host))
' add verbs to the MenuCommandService in HostSurface class
Dim menucommandService As IMenuCommandService =
GetService(GetType(IMenuCommandService))
Dim m_Verb As DesignerVerb = New DesignerVerb("First Designer Verb(",
AddressOf OnFirstItemSelected)
menucommandService.AddVerb(m_Verb)
m_Verb = New DesignerVerb("Second Designer Verb", AddressOf
OnSecondItemSelected)
menucommandService.AddVerb(m_Verb)
End Sub
Private Sub OnFirstItemSelected(ByVal sender As Object, ByVal arge As
EventArgs)
' Display a message
System.Windows.Forms.MessageBox.Show("The first designer verb was invoked.")
End Sub
Private Sub OnSecondItemSelected(ByVal sender As Object, ByVal args As
EventArgs)
' Display a message
System.Windows.Forms.MessageBox.Show("The second designer verb was
invoked.")
End Sub
End Class
Post by Linda Liu [MSFT]Hi David,
In order to get a menu to pop up when right-clicking on a custom design
surface, you should do two things. The first thing is to create a class
implementing the Interface IMenuCommandService and add the service to the
custom design surface. The second thing is to add verbs to the
MenuCommandService.
Suppose you have opened a form in the custom design surface and added
several controls on the form. When you right-click on the controls or the
form in your custom design surface, a context menu will appear.
If you want to have a different menu item when you right-click on a custom
control in your custom design surface, you should associate a designer to
your custom control and override the Verbs property of the designer class.
The following is a sample.
// the definition of a class implementing IMenuCommandService
public class MenuCommandServiceImpl : IMenuCommandService
{
// the host
private IDesignerHost host;
// commandId-command mapping
private IDictionary commands;
// menuItem-verb mapping
private IDictionary menuItemVerb;
// the global verbs collection.
private DesignerVerbCollection globalVerbs;
// we use the same context menu over-and-over
private ContextMenu contextMenu;
// we keep the lastSelectedComponent around
private IComponent lastSelectedComponent;
public MenuCommandServiceImpl(IDesignerHost host)
{
this.host = host;
commands = new Hashtable();
globalVerbs = new DesignerVerbCollection();
menuItemVerb = new Hashtable();
contextMenu = new ContextMenu();
lastSelectedComponent = null;
}
#region Implementation of IMenuCommandService
/// called to add a MenuCommand
public void AddCommand(System.ComponentModel.Design.MenuCommand
command)
{
if (command == null)
{
throw new ArgumentException("command");
}
// don't add commands twice
if (FindCommand(command.CommandID) == null)
{
commands.Add(command.CommandID, command);
}
}
/// called to remove a MenuCommand
public void RemoveCommand(System.ComponentModel.Design.MenuCommand
command)
{
if (command == null)
{
throw new ArgumentException("command");
}
commands.Remove(command.CommandID);
}
/// called when to add a global verb
public void AddVerb(System.ComponentModel.Design.DesignerVerb verb)
{
if (verb == null)
{
throw new ArgumentException("verb");
}
globalVerbs.Add(verb);
// create a menu item for the verb and add it to the context
menu
MenuItem menuItem = new MenuItem(verb.Text);
menuItem.Click += new EventHandler(MenuItemClickHandler);
menuItemVerb.Add(menuItem, verb);
contextMenu.MenuItems.Add(menuItem);
}
/// called to remove global verb
public void RemoveVerb(System.ComponentModel.Design.DesignerVerb
verb)
{
if (verb == null)
{
throw new ArgumentException("verb");
}
globalVerbs.Remove(verb);
// find the menu item associated with the verb
MenuItem associatedMenuItem = null;
foreach (DictionaryEntry de in menuItemVerb)
{
if (de.Value == verb)
{
associatedMenuItem = de.Key as MenuItem;
break;
}
}
// if we found the verb's menu item, remove it
if (associatedMenuItem != null)
{
menuItemVerb.Remove(associatedMenuItem);
}
// remove the verb from the context menu too
contextMenu.MenuItems.Remove(associatedMenuItem);
}
/// returns the MenuCommand associated with the commandId.
public System.ComponentModel.Design.MenuCommand
FindCommand(System.ComponentModel.Design.CommandID commandID)
{
return commands[commandID] as MenuCommand;
}
/// called to invoke a command
public bool GlobalInvoke(System.ComponentModel.Design.CommandID
commandID)
{
bool result = false;
MenuCommand command = FindCommand(commandID);
if (command != null)
{
command.Invoke();
result = true;
}
return result;
}
/// called to show the context menu for the selected component.
public void ShowContextMenu(System.ComponentModel.Design.CommandID
menuID, int x, int y)
{
ISelectionService selectionService =
host.GetService(typeof(ISelectionService)) as ISelectionService;
// get the primary component
IComponent primarySelection = selectionService.PrimarySelection
as IComponent;
// if the he clicked on the same component again then just show
the context
// menu. otherwise, we have to throw away the previous
// set of local menu items and create new ones for the newly
// selected component
if (lastSelectedComponent != primarySelection)
{
// remove all non-global menu items from the context menu
ResetContextMenu();
// get the designer
IDesigner designer = host.GetDesigner(primarySelection);
// not all controls need a desinger
if (designer != null)
{
// get designer's verbs
DesignerVerbCollection verbs = designer.Verbs;
foreach (DesignerVerb verb in verbs)
{
// add new menu items to the context menu
CreateAndAddLocalVerb(verb);
}
}
}
// we only show designer context menus for controls
if (primarySelection is Control)
{
Control comp = primarySelection as Control;
Point pt = comp.PointToScreen(new Point(0, 0));
contextMenu.Show(comp, new Point(x - pt.X, y - pt.Y));
}
// keep the selected component for next time
lastSelectedComponent = primarySelection;
}
/// returns the the current designer verbs
public System.ComponentModel.Design.DesignerVerbCollection Verbs
{
get
{
// create a new collection
DesignerVerbCollection availableVerbs = new
DesignerVerbCollection();
// add the global verbs
if (globalVerbs != null && globalVerbs.Count > 0)
{
availableVerbs.AddRange(globalVerbs);
}
// now add the local verbs
ISelectionService selectionService =
host.GetService(typeof(ISelectionService)) as ISelectionService;
IComponent primaryComponent =
selectionService.PrimarySelection as IComponent;
if (primaryComponent != null)
{
IDesigner designer =
host.GetDesigner(primaryComponent);
if (designer != null && designer.Verbs != null &&
designer.Verbs.Count > 0)
{
availableVerbs.AddRange(designer.Verbs);
}
}
return availableVerbs;
}
}
#endregion
// called to invoke menu item verbs
private void MenuItemClickHandler(object sender, EventArgs e)
{
// get the menu item
MenuItem menuItem = sender as MenuItem;
if (menuItem != null)
{
// get and invoke the verb
DesignerVerb verb = menuItemVerb[menuItem] as DesignerVerb;
if (verb != null)
{
try
{
verb.Invoke();
}
catch { }
}
}
}
// removes all local verbs from the context menu
private void ResetContextMenu()
{
if (contextMenu != null && contextMenu.MenuItems != null &&
contextMenu.MenuItems.Count > 0)
{
MenuItem[] menuItemArray = new
MenuItem[contextMenu.MenuItems.Count];
contextMenu.MenuItems.CopyTo(menuItemArray, 0);
foreach (MenuItem menuItem in menuItemArray)
{
// if its not in the global list, remove it
if (!IsInGlobalList(menuItem.Text))
{
contextMenu.MenuItems.Remove(menuItem);
}
// get rid of the menu item from the mapping
menuItemVerb.Remove(menuItem);
}
}
}
// creats and adds a local verb
private void CreateAndAddLocalVerb(DesignerVerb verb)
{
if (verb == null)
{
throw new ArgumentException("verb");
}
VerifyVerb(verb);
// create a menu item for the verb
MenuItem menuItem = new MenuItem(verb.Text);
// attach the menu item click listener
menuItem.Click += new EventHandler(MenuItemClickHandler);
// do the menuItem-verb mapping
menuItemVerb.Add(menuItem, verb);
// add to context menu
contextMenu.MenuItems.Add(menuItem);
}
// returns true if the verb is in the global verb collection
private bool IsInGlobalList(string verbText)
{
bool found = false;
if (globalVerbs != null && globalVerbs.Count > 0)
{
foreach (DesignerVerb dv in globalVerbs)
{
if (string.Compare(dv.Text, verbText, true) == 0)
{
found = true;
break;
}
}
}
return found;
}
// we can't add the same verb twice
private void VerifyVerb(DesignerVerb verb)
{
if (verb == null)
{
throw new ArgumentException("verb");
}
// make sure the verb is not in the global list
if (globalVerbs != null && globalVerbs.Count > 0)
{
foreach (DesignerVerb dv in globalVerbs)
{
if (string.Compare(dv.Text, verb.Text, true) == 0)
{
throw new Exception("Cannot add the same verb
twice.");
}
}
}
// now check the menuItemVerb mapping
if (menuItemVerb != null && menuItemVerb.Count > 0)
{
foreach (DesignerVerb dv in menuItemVerb.Values)
{
if (string.Compare(dv.Text, verb.Text, true) == 0)
{
throw new Exception("Cannot add the same verb
twice.");
}
}
}
}
}
// add the MenuCommandService to the class HostSurface
public class HostSurface : DesignSurface
{
public HostSurface() : base()
{
IDesignerHost host = (IDesignerHost)this.GetService(typeof
IDesignerHost));
this.AddService(typeof(IMenuCommandService), new
MenuCommandServiceImpl(host));
// add verbs to the MenuCommandService in HostSurface class
IMenuCommandService _menucommandService =
(IMenuCommandService)this.GetService(typeof(IMenuCommandService));
DesignerVerb m_Verb = new DesignerVerb("First Designer
Verb", new EventHandler(OnFirstItemSelected));
_menucommandService.AddVerb(m_Verb);
m_Verb = new DesignerVerb("Second Designer Verb", new
EventHandler(OnSecondItemSelected));
_menucommandService.AddVerb(m_Verb);
}
private void OnFirstItemSelected(object sender, EventArgs args)
{
// Display a message
System.Windows.Forms.MessageBox.Show("The first designer verb
was invoked.");
}
private void OnSecondItemSelected(object sender, EventArgs args)
{
// Display a message
System.Windows.Forms.MessageBox.Show("The second designer verb
was invoked.");
}
}
Hope this is helpful to you.
If you have any other concerns or need anything else, please feel free to
let me know.
Sincerely,
Linda Liu
Microsoft Online Community Support
====================================================
When responding to posts,please "Reply to Group" via
your newsreader so that others may learn and benefit
from your issue.
====================================================