How to Use the Context Menu Feature of Silverlight 4
Tips & TricksSummary
Description
Since yFiles for Silverlight 1.0 has been created for Silverlight 3 before version 4 of the runtime was released, it does not have integrated support for the Silverlight 4 Toolkit's context menu. Nevertheless, the context menu feature can be used with the 1.0 version of yFiles for Silverlight using the following example code.
MainInputMode, or its subclass GraphEditorInputMode can host a specialized child input mode easily:
var editorInputMode = new GraphEditorInputMode();
var contextMenuInputMode = new ContextMenuInputMode()
{ Menu = new ContextMenu() {
Items = { new MenuItem() {Header = "Test"} }
}};
editorInputMode.AddConcurrent(contextMenuInputMode);
Below is the implementation of the input mode used in the above snippet.
/// <summary>
/// An implementation of the <see cref="IInputMode"/> interface that will
/// display a <see cref="ContextMenu"/> when the user right clicks on the
/// <see cref="CanvasControl"/>.
/// </summary>
public class ContextMenuInputMode : AbstractConcurrentInputMode
{
private ContextMenu menu;
private MouseEventArgs lastMouseEventArgs;
/// <summary>
/// Gets or sets the menu to show.
/// </summary>
/// <remarks>
/// If no menu has been configured with this instance this will
/// create the menu using the <see cref="CreateMenu"/> callback.
/// </remarks>
[CanBeNull]
public ContextMenu Menu {
get {
if (menu == null) {
menu = CreateMenu();
menu.Opened += Menu_OnOpen;
menu.Closed += Menu_OnClosed;
}
return menu;
}
set {
if (menu != null) {
if (menu.IsOpen) {
menu.IsOpen = false;
ReleaseMutex();
}
menu.Opened -= Menu_OnOpen;
menu.Closed -= Menu_OnClosed;
}
menu = value;
if (menu != null) {
menu.Opened += Menu_OnOpen;
menu.Closed += Menu_OnClosed;
if (Installed) {
ContextMenuService.SetContextMenu(Canvas, menu);
}
}
}
}
private void Menu_OnOpen(object sender, RoutedEventArgs e) {
ContextMenu menu = sender as ContextMenu;
if (menu != null && Canvas != null) {
if (CanRequestMutex()) {
if (lastMouseEventArgs != null) {
var position = lastMouseEventArgs.GetPosition(Canvas.GetContentHost());
var worldCoordinates = Canvas.ToWorldCoordinates(position);
bool cancel = !OnPopulateContextMenu(menu, worldCoordinates);
if (!cancel) {
RequestMutex();
return;
}
}
}
menu.IsOpen = false;
Canvas.Focus();
}
}
private void Menu_OnClosed(object sender, RoutedEventArgs routedEventArgs) {
ReleaseMutex();
if (Canvas != null) {
Canvas.Focus();
}
}
/// <summary>
/// This will populate the context menu for the given world coordinate.
/// </summary>
/// <param name="menu">The menu to optionally populate with items.</param>
/// <param name="position">The position in the world coordinate system for which
/// the context menu has been invoked.</param>
/// <returns>Whether to show the context menu.</returns>
protected virtual bool OnPopulateContextMenu(ContextMenu menu, PointD position) {
return true;
}
/// <summary>
/// Creates a new instance with no initial context menu.
/// </summary>
/// <seealso cref="CreateMenu"/>
/// <seealso cref="Menu"/>
public ContextMenuInputMode()
: this(null) {
}
/// <summary>
/// Creates a new instance using the provided menu.
/// </summary>
/// <param name="menu">The menu to show.</param>
public ContextMenuInputMode([CanBeNull] ContextMenu menu) {
Menu = menu;
}
/// <summary>
/// Factory method that creates the initial menu strip.
/// </summary>
/// <remarks>
/// Subclasses may override this with more sophisticated implementations.
/// </remarks>
/// <returns>An empty <see cref="ContextMenu"/></returns>
/// <seealso cref="Menu"/>
protected virtual ContextMenu CreateMenu() {
return new ContextMenu();
}
/// <summary>
/// Installs this mode in the canvas, registering the <see cref="Menu"/>
/// as the canvas' <see cref="ContextMenu"/>
/// </summary>
/// <param name="context">The canvas to install this mode into.</param>
public override void Install(IInputModeContext context) {
base.Install(context);
ContextMenuService.SetContextMenu(Canvas, Menu);
Canvas.MouseMove += OnMouseMoved;
}
void OnMouseMoved(object sender, MouseEventArgs me) {
lastMouseEventArgs = me;
}
/// <summary>
/// Makes the menu invisible.
/// </summary>
public override void Cancel() {
if (menu != null) {
menu.IsOpen = false;
}
base.Cancel();
}
/// <summary>
/// Makes the menu invisible.
/// </summary>
/// <returns><c>base.Stop()</c></returns>
public override bool Stop() {
if (menu != null) {
menu.IsOpen = false;
}
return base.Stop();
}
/// <summary>
/// Removes the menu from the context and replaces it with the old instance.
/// </summary>
/// <param name="context">The context to uninstall this mode from.</param>
public override void Uninstall(IInputModeContext context) {
Canvas.MouseMove -= OnMouseMoved;
ContextMenuService.SetContextMenu(Canvas, null);
base.Uninstall(context);
}
}