WPF:如何在仅使用`StaysOpen =“False”`还不够的极端情况下自动关闭弹出窗口?

问题描述 投票:0回答:1

我创建了一个自定义时间选择器控件,该控件在以按钮为中心的

Popup
中打开,并且运行良好。我无法解决的唯一问题是,当
Popup
打开并且用户移动包含我的控件的窗口(通过拖动其标题栏)或当它们滚动到我的控件之外时,
Popup
不会关闭,只是停留在原来的位置。

我已经设置了

StaysOpen="False"
,除了上面提到的两种情况之外,这对所有情况都非常有效:单击应用程序中的任意位置(标题栏区域除外)将导致弹出窗口关闭。

根据文档:

StaysOpen
false
时,
Popup
控件拦截所有鼠标和键盘事件,以确定这些事件之一何时发生在
Popup
控件外部。

但是,这似乎并不意味着我想的那样:当我将事件处理程序分配给

Popup
MouseWheel
PreviewMouseWheel
MouseDown
等时,这永远不会因任何鼠标活动而触发在
Popup
之外。也许文档意味着它仅在内部拦截它们?如果是这样,它就无法捕获这些极端情况......

我已经在这里阅读了大量有关该主题的已接受答案,但它们要么对我没有影响(无论出于何种原因),要么他们通过实际捕获控件代码之外的鼠标事件来处理此问题,然后告诉控件关闭其

Popup
。这对我来说不是一个选择,因为我希望我的控件可以在任何父级上重复使用,而不需要消费者添加用于关闭鼠标活动上的
Popup
的代码...实现此工作所需的所有内容都需要成为我的控件,无论是它的代码还是它的样式/模板。

我想出了一种解决滚动问题的方法,方法是从我的控制中向上移动视觉树,直到找到

ScrollViewer
,然后将自己附加到其
ScrollChanged
事件,这似乎至少适用于简单的情况但还是感觉有点hacky...

我不知道如何处理标题栏问题...

还有更多想法或指点吗?

wpf popup
1个回答
0
投票

很奇怪,您报告输入事件未引发。如果是这种情况,那么

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;
}
© www.soinside.com 2019 - 2024. All rights reserved.