如何在控件外部单击时关闭Silverlight中的弹出窗口?
在我的Silverlight UI中,我有一个按钮,当单击时会弹出一个带有一些过滤参数的控件。 当你在它外面点击时,我希望这个控件隐藏起来。 换句话说,它应该以类似于combobox的方式运行,但它不是combobox(您不选择其中的项目)。 这是我试图捕获控件外部的点击以解除它的方式:
public partial class MyPanel : UserControl { public MyPanel() { InitializeComponent(); } private void FilterButton_Click(object sender, RoutedEventArgs e) { // Toggle the open state of the filter popup FilterPopup.IsOpen = !FilterPopup.IsOpen; } private void UserControl_Loaded(object sender, RoutedEventArgs e) { // Capture all clicks and close the popup App.Current.RootVisual.MouseLeftButtonDown += delegate { FilterPopup.IsOpen = false; }; } }
不幸的是, MouseLeftButtonDown
的事件处理程序永远不会被触发。 是否有一种完善的方法可以使弹出控件在您点击它之外时自动解除? 如果没有,为什么我的MouseLeftButtonDown
处理程序不会被触发?
解:
我想我会发布我的整个解决方案以防其他人觉得它有用。 在我的顶级视觉中,我为弹出窗口声明了一个“盾牌”,如下所示:
然后,我为Popup
类添加了一个扩展方法,如下所示:
public static class PopupUtils { public static void MakeAutoDismissing(this Popup popup) { var shield = (App.Current.RootVisual as MainPage).PopupShield; // Whenever the popup opens, deploy the shield popup.HandlePropertyChanges( "IsOpen", (s, e) => { shield.Visibility = (bool)e.NewValue ? Visibility.Visible : Visibility.Collapsed; } ); // Whenever the shield is clicked, dismiss the popup shield.MouseLeftButtonDown += (s, e) => popup.IsOpen = false; } } public static class FrameworkUtils { public static void HandlePropertyChanges( this FrameworkElement element, string propertyName, PropertyChangedCallback callback) { //Bind to a depedency property Binding b = new Binding(propertyName) { Source = element }; var prop = System.Windows.DependencyProperty.RegisterAttached( "ListenAttached" + propertyName, typeof(object), typeof(UserControl), new System.Windows.PropertyMetadata(callback)); element.SetBinding(prop, b); } }
扩展方法使用如下:
private void UserControl_Loaded(object sender, RoutedEventArgs e) { FilterPopup.MakeAutoDismissing(); }
您是否在RootVisual上设置了背景颜色?
一种方法是将控件放在填充整个Silverlight表面的透明canvas上。 单击canvas时,关闭canvas并控制。 如果要接收鼠标事件,确保将canvas的背景画笔设置为“透明”非常重要。
我没有成功的另一种方法是在Silverlight中使用鼠标捕获并检测何时在弹出窗口外单击鼠标。
我刚刚创造了类似的东西,希望这可以提供任何帮助。 🙂
在我的PopUp控件中,我有一个Border,其中包含一个包含许多文本框的Grid。 我将Border命名为’PopUpBorder’。
在我的UserControl的构造函数中,我有,
this.PopUpBorder.MouseLeave += (s, e) => { Application.Current.RootVisual.MouseLeftButtonDown += (s1, e1) => { this.PopUp.IsOpen = false; }; };
看起来它按预期工作。 如果这不适用于您的情况,请告诉我。
在第一次单击时,调用控件上的CaptureMouse()方法。 然后在第二次单击时调用ReleaseMouseCapture()。
建议的解决方案使用特殊的屏蔽canvas来阻止对其他控件的输入。 这很好,但我正在尝试实现一个通用控件,不能依赖于这样的盾牌canvas的存在。 MouseLeftButtonDown事件在root visual上的绑定对我来说也不起作用,因为我canvas上的现有按钮仍会触发自己的click事件,而不是根视觉上的MouseLeftButtonDown。 我在这篇冰文章中找到了解决方案:来自Kent Boograart的Silverlight中的 Popup.StaysOpen。 这个问题的主要方法是:
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; } }
我仍然不确定,为什么这个解决方案适合我,而不是这个:
Application.Current.RootVisual.MouseLeftButtonDown += (s1, e1) => { this.PopUp.IsOpen = false; };
看起来FindHighestAncestor方法只返回root视觉效果? 但那么差异必须是事件处理程序? 我猜主要的区别是AddHandler方法的最后一个参数,在这种情况下是真的:
AddHandler(Windows.Popup.MouseLeftButtonDownEvent, (MouseButtonEventHandler)OnMouseLeftButtonDown, true);
MSDN文档说:
handledEventsToo
类型: System.Boolean
注册处理程序,以便即使路由事件在其事件数据中标记为已处理,也会调用该函数; 如果路由事件已标记为已处理,则使用默认条件注册处理程序为false,以使其不被调用。 默认值为false。 不要经常要求重新处理路由事件。 有关更多信息,请参阅备注。
一个更简单的替代方法(虽然不是你要求的)将是关闭MouseLeave上的弹出窗口。 Popup对象上的MouseLeave本身不起作用,但是Popup中最高级别容器上的MouseLeave会起作用。
我已经在我的博客上发布了一个解决方案,你可以看到这里的post – Silverlight:点击外面点击Popup 。 正如您所看到的,使用非常简单 – 它是您在弹出窗口中添加的附加属性。 你没有必要添加任何包装,如果你有或没有背景颜色,你不必小心……我的代码会照顾好这一切。
- 使用iTextSharp将图像转换为PDF保留剪切路径
- entity framework7 RC 1和ASP.NET MVC 6中的种子初始数据
- 在.net win表单中是否有任何validation控件可用,如asp.net web表单?
- 有没有简单的方法来创建方法并在C#中动态设置它的主体?
- C#插值字符串中的可选参数是什么?
- WindowsForms的每像素碰撞检测算法
- 将ResourceDictionary添加到类库
- C#查找搜索结果显示的相关文档片段
- 什么是BeginReceive(Byte(),Int32,Int32,SocketFlags,AsyncCallback,Object)和socket中的回调方法?