In my Silverlight UI, I have a button that when clicked pops up a control with some filtering parameters. I would like this control to hide itself when you click outside of
Did you set a background color on your RootVisual?
A simpler alternative (although not exactly what you asked for) would be to close the popup on MouseLeave. MouseLeave on the Popup object itself wont work, but MouseLeave on the highest level container within the Popup does.
On the first click, call the CaptureMouse() method on the control. Then call ReleaseMouseCapture() on the second click.
i have posted a solution for this on my blog, you can see the post here - Silverlight: close Popup on click outside. as you can see the use is very simple - it is an attached property you add on your popup. you do not have to add any wrappers, you do not have to take care if you do or do not have some background color... my code will take care of it all.
The proposed solution uses a special shield canvas to block input to other controls. This is fine, but I am trying implement a generic control and cannot depend on the existence of such a shield canvas. The binding of the MouseLeftButtonDown event on the root visual didn't work for me either, because existing buttons on my canvas would still fire their own click event and not the MouseLeftButtonDown on the root visual. I found the solution in this ice article: Popup.StaysOpen in Silverlight from Kent Boograart. The main methods for this question are:
private void OnPopupOpened(object sender, EventArgs e)
{
var popupAncestor = FindHighestAncestor(this.popup);
if (popupAncestor == null)
{
return;
}
popupAncestor.AddHandler(Windows.Popup.MouseLeftButtonDownEvent, (MouseButtonEventHandler)OnMouseLeftButtonDown, true);
}
private void OnPopupClosed(object sender, EventArgs e)
{
var popupAncestor = FindHighestAncestor(this.popup);
if (popupAncestor == null)
{
return;
}
popupAncestor.RemoveHandler(Windows.Popup.MouseLeftButtonDownEvent, (MouseButtonEventHandler)OnMouseLeftButtonDown);
}
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// in lieu of DependencyObject.SetCurrentValue, this is the easiest way to enact a change on the value of the Popup's IsOpen
// property without overwriting any binding that may exist on it
var storyboard = new Storyboard() { Duration = TimeSpan.Zero };
var objectAnimation = new ObjectAnimationUsingKeyFrames() { Duration = TimeSpan.Zero };
objectAnimation.KeyFrames.Add(new DiscreteObjectKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero), Value = false });
Storyboard.SetTarget(objectAnimation, this.popup);
Storyboard.SetTargetProperty(objectAnimation, new PropertyPath("IsOpen"));
storyboard.Children.Add(objectAnimation);
storyboard.Begin();
}
private static FrameworkElement FindHighestAncestor(Popup popup)
{
var ancestor = (FrameworkElement)popup;
while (true) {
var parent = VisualTreeHelper.GetParent(ancestor) as FrameworkElement;
if (parent == null) {
return ancestor;
}
ancestor = parent;
}
}
I'm still not sure, why this solution works for me and this not:
Application.Current.RootVisual.MouseLeftButtonDown += (s1, e1) =>
{
this.PopUp.IsOpen = false;
};
It looks like the FindHighestAncestor method will just return the root visual? But then the difference must be the event handler? I guess the main difference is the last parameter of the AddHandler method which is true in the case:
AddHandler(Windows.Popup.MouseLeftButtonDownEvent, (MouseButtonEventHandler)OnMouseLeftButtonDown, true);
The MSDN docs say:
handledEventsToo
Type: System.Boolean
true to register the handler such that it is invoked even when the routed event is marked handled in its event data; false to register the handler with the default condition that it will not be invoked if the routed event is already marked handled. The default is false. Do not routinely ask to rehandle a routed event. For more information, see Remarks.
I just created something similar and hopefully this could be of any help. :)
In my PopUp control, I have a Border which has a Grid that contains a number of textboxes. I named the Border 'PopUpBorder'.
In my UserControl's constructor, I have,
this.PopUpBorder.MouseLeave += (s, e) =>
{
Application.Current.RootVisual.MouseLeftButtonDown += (s1, e1) =>
{
this.PopUp.IsOpen = false;
};
};
And looks like it is working as expected. Please let me know if this doesn't work in your case.