Right-Clicking in Silverlight 2 — A Refinement

Shout it kick it on DotNetKicks.com

The Problem

If you search for "Right Click Silverlight 2" in Google, you will get several hits.  There are several ways to achieve this functionality, and they all have two things in common that I object to:

1. They use Javascript to send a message to the Silverlight application.  While there is no way (that I know of) around this,  requiring the hosting HTML to include this code is what I object to.  Moving the app to a new page requires the same hookups that the developer may or may not remember to do.

2. They send events around that require a hacky feel — violating encapsulation and separations of concern (SOC) in a way that makes my skin itch.   Any complex application with user controls strewn throughout will feel the pain of these approaches.

This article solves both of these problems:

First, don’t require the user to write the Javascript every time.  Instead let the Silverlight code execute the javascript that includes the necessary browser functionality.

Second, instead of sending events around, or passing arguments through your user controls, use reflection to find the controls that care about right-clicks.  This is the fun part.  There exists a neat little static method called VisualTreeHelper.FindElementsInHostCoordinates().  You pass it the mouse coordinates, and it returns a list of all of the controls that occupy space at that point.  Then, just find any controls that implement the interface IRightClickable and call the method.

The Code

public class RightClickHandler
   public interface IRightClickable
       void OnRightClick(Point point);

   private RightClickHandler()

   private static readonly RightClickHandler Instance = new RightClickHandler();

   private static bool Initialized { get; set; }
   public static void Initialize()

              "function OnClick(e) {" +
              "    if (!e) e = window.event;" +
              "    var silverlightControl = document.getElementById('" + HtmlPage.Plugin.Id + "');" +
              "    if (silverlightControl) silverlightControl.Content.RightClickListener.ProcessMouseEvent(e);" +
              "}" +
              "document.onmousedown = OnClick;" +
              "document.oncontextmenu = function() { return false; }");

       HtmlPage.RegisterScriptableObject("RightClickListener", Instance);

       Initialized = true;

   public void ProcessMouseEvent(ScriptObject evt)
       int button = int.Parse(evt.GetProperty("button").ToString());
       if (button == 2)
           var location = new Point
               X = double.Parse(evt.GetProperty("clientX").ToString()),
               Y = double.Parse(evt.GetProperty("clientY").ToString())

           foreach (IRightClickable element in FindElements<IRightClickable>(location))

   private static IEnumerable<T> FindElements<pT>(Point absoluteLocation) where T : class
       return from element in VisualTreeHelper.FindElementsInHostCoordinates(absoluteLocation, Application.Current.RootVisual)
              where element is T
              select element as T;

The Usage

Simply initialize the RightClickHandler code somewhere (It can be called more than once without any ill effects), and implement the interface.  If the control is right-clicked, OnRightClick() will be called!

public partial class TestControl : UserControl, RightClickHandler.IRightClickable
   public TestControl()

   public void OnRightClick(Point point)
       Input.Text = "Clicked: (" + point.X + ", " + point.Y + ")";

Windowless Mode

It seems that there is no way around this.  The Silverlight plugin must be created as "Windowless" in order to receive right clicks.  I would have loved to encapsulate this in the RightClickHandler class, but it seems that there is no way.  In your aspx, you would have:

<asp:Silverlight Windowless="true" runat="server" ... 

If you are using straight HTML, you would add this parameter:

<param name="windowless" value="true" />

Leave a Reply