我创建了一个自定义时间选择器控件,该控件在以按钮为中心的
Popup
中打开,并且运行良好。我无法解决的唯一问题是,当 Popup
打开并且用户移动包含我的控件的窗口(通过拖动其标题栏)或当它们滚动到我的控件之外时, Popup
不会关闭,只是停留在原来的位置。
我已经设置了
StaysOpen="False"
,除了上面提到的两种情况之外,这对所有情况都非常有效:单击应用程序中的任意位置(标题栏区域除外)将导致弹出窗口关闭。
根据文档:
当
为StaysOpen
时,false
控件拦截所有鼠标和键盘事件,以确定这些事件之一何时发生在Popup
控件外部。Popup
但是,这似乎并不意味着我想的那样:当我将事件处理程序分配给
Popup
的 MouseWheel
、PreviewMouseWheel
、MouseDown
等时,这永远不会因任何鼠标活动而触发在 Popup
之外。也许文档意味着它仅在内部拦截它们?如果是这样,它就无法捕获这些极端情况......
我已经在这里阅读了大量有关该主题的已接受答案,但它们要么对我没有影响(无论出于何种原因),要么他们通过实际捕获控件代码之外的鼠标事件来处理此问题,然后告诉控件关闭其
Popup
。这对我来说不是一个选择,因为我希望我的控件可以在任何父级上重复使用,而不需要消费者添加用于关闭鼠标活动上的 Popup
的代码...实现此工作所需的所有内容都需要成为我的控件,无论是它的代码还是它的样式/模板。
我想出了一种解决滚动问题的方法,方法是从我的控制中向上移动视觉树,直到找到
ScrollViewer
,然后将自己附加到其 ScrollChanged
事件,这似乎至少适用于简单的情况但还是感觉有点hacky...
我不知道如何处理标题栏问题...
还有更多想法或指点吗?
很奇怪,您报告输入事件未引发。如果是这种情况,那么
Popup
将无法对此类事件做出反应以实现 StyaysOpen 功能。
创建自定义弹出窗口,然后覆盖相关的鼠标事件处理程序就可以了。
要检测窗口拖动,您必须获取所属的
Window
实例并观察 Window.LocationChanged
事件:
MyPopup.cs
public class MyPopup : Popup
{
private Window Host { get; set; }
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
this.Host = Window.GetWindow(this);
}
protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
{
base.OnPreviewMouseWheel(e);
this.IsOpen = this.StaysOpen || this.IsMouseDirectlyOver;
}
protected override void OnOpened(EventArgs e)
{
base.OnOpened(e);
this.Host.LocationChanged += OnHostLocationChanged;
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
this.Host.LocationChanged -= OnHostLocationChanged;
}
private void OnHostLocationChanged(object? sender, EventArgs e)
=> this.IsOpen = this.StaysOpen;
}