Introduction
Recently in one of the projects I was working on, we had the need to disable the Silverlight configuration dialog box which appears automatically when the user clicks anywhere on the Silverlight application as shown below:
The Business Problem
The Silverlight configuration dialog has got a lot of useful functionality and the ability to quickly uninstall the application. However most of the time, you would want to prevent the business users from knowing the details of the implementation. There is also a possibility that the business users could change a few settings inadvertently and cause the application from working or worse they could uninstall the application without knowing to install the application back again.
The image bellows shows a test solution which shows the right click menu which appears by default.
Solution
One possible approach for solving this problem would be to use JavaScript to disable the right click at the plugin level. This approach however would disable the right click event for the entire application and will not work in out of browser mode. The solution presented in this article uses the right click event handler exposed in the Silverlight 4 version and will not work in the previous versions of Silverlight. The solution is to add an event handler to the mouse right button down event in the application startup method. In the event handler, we set the ishandled
property to true
. This essentially prevents the event from bubbling up all the way to the Silverlight plugin. The source code for the same is shown below:
private void Application_Startup(object sender, StartupEventArgs e)
{
Application.Current.RootVisual.MouseRightButtonDown +=
new System.Windows.Input.MouseButtonEventHandler
(RootVisual_MouseRightButtonDown);
}
void RootVisual_MouseRightButtonDown
(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
}
private void Application_Exit(object sender, EventArgs e)
{
Application.Current.RootVisual.MouseRightButtonDown -=
new System.Windows.Input.MouseButtonEventHandler
(RootVisual_MouseRightButtonDown);
}
It appears to be a easy workaround. However, we still have a problem. It can be observed that when the datepicker popup window is open, the user could right click into the popup control and get the configuration dialog.
The problem is shown below:
After spending some time, I got the information from the MSDN documentation that certain objects in Silverlight participate in a relation with the primary tree conceptually like an overlay over the main visuals. These objects are not part of the usual parent-child relationships that connect all tree elements to the visual root. This is the case for any displayed Popup or ToolTip. If you want to handle routed events from a Popup or ToolTip, you should place the handlers on specific UI elements that are within the Popup or ToolTip and not the Popup or ToolTip elements themselves. You should not rely on routing inside any compositing that is performed for Popup or ToolTip content. This is because event routing for routed events works only along the main visual tree. Popup or ToolTip is not considered a parent of subsidiary UI elements and never receives the routed event, even if it is trying to use something like the Popup default background as the capture area for input events.
So in order to solve the problem completely, any control that uses a popup like date picker must be overridden to use a custom control that explicitly overrides the right click behaviour of the popup and sets the right click event as handled. An example for a custom time picker control is shown below:
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace Controls
{
public class CustomTimePicker:TimePicker
{
private Canvas _canvas = null;
private Popup _p = null;
internal Canvas PopupCanvas
{
get { return _canvas; }
set
{
if (_canvas != null)
{
_canvas.MouseRightButtonDown -=
new MouseButtonEventHandler(PopupCanvas_MouseRightButtonDown);
}
_canvas = value;
if (_canvas != null)
{
_canvas.MouseRightButtonDown +=
new MouseButtonEventHandler(PopupCanvas_MouseRightButtonDown);
}
}
}
internal Popup TimepickerPopup
{
get { return _p; }
set
{
_p = value;
}
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
TimepickerPopup = GetTemplateChild("Popup") as Popup;
if (TimepickerPopup != null)
{
PopupCanvas = TimepickerPopup.Child as Canvas;
}
if (PopupCanvas != null)
{
PopupCanvas.MouseRightButtonDown += new MouseButtonEventHandler
(PopupCanvas_MouseRightButtonDown);
}
}
void PopupCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
}
}
}
Reference
History
- Initial draft version V1.0 (08-Feb-2011)