【问题标题】:Popup with "StaysOpen=false" steals LeftMouseButtonDown event带有“StaysOpen=false”的弹出窗口窃取 LeftMouseButtonDown 事件
【发布时间】:2015-07-05 13:04:30
【问题描述】:

在我的应用程序中,当用户尝试单击主窗口上的滑块时,当弹出控件打开时,弹出控件会窃取鼠标按下事件。 这会导致滑块无法正确响应鼠标按下事件。 (它似乎获得焦点并移动到错误的位置)

我发现当弹出窗口的“StaysOpen”属性为假(并且弹出窗口已打开)时,滑块中的“OnPreviewMouseLeftButtonDown”不会触发, 并在其为真时(或弹出窗口关闭时)触发。

我想知道是否有人找到了解决此问题的方法。

我在我的应用程序的其他控件中遇到了这些类型的问题,在各种情况下,所以我更喜欢更通用的解决方案,而不是仅仅为滑块解决这个问题。

示例代码:

<Window x:Class="SampleApplication.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="Root"
    Title="MainWindow" Height="350" Width="525">
<Grid Height="130" Width="300">
    <Button Width="40" Height="40" Click="ButtonBase_OnClick" HorizontalAlignment="Left" VerticalAlignment="Top"></Button>
    <Popup StaysOpen="False" IsOpen="{Binding ElementName=Root, Path=IsOpen}" Width="100" Height="100"
           HorizontalAlignment="Center" VerticalAlignment="Center" Placement="Center">
        <Grid Background="Black">
            <TextBlock Text="hello"></TextBlock>
        </Grid>
    </Popup>
    <Slider Width="200" IsMoveToPointEnabled="True" VerticalAlignment="Bottom"></Slider>
</Grid>

谢谢你, 尤塔姆

【问题讨论】:

  • 我最好的猜测是,发生这种情况是因为当单击其他内容时隐藏的弹出窗口有一个事件处理程序,它不会正确路由事件。但我也不知道如何解决这个问题wpftutorial.net/RoutedEvents.html

标签: c# wpf xaml popup slider


【解决方案1】:

发生这种情况是因为PreviewMouseDown(及其派生类)(来自基类UIElement)有一个默认RoutingStrategy.Direct

Direct - 路由事件不通过元素树路由,但支持其他路由事件功能,例如类处理、EventTrigger 或 EventSetter。

这是取自ReferenceSource的事件源代码。

public static readonly RoutedEvent PreviewMouseLeftButtonDownEvent =
    EventManager.RegisterRoutedEvent(
         "PreviewMouseLeftButtonDown",
         RoutingStrategy.Direct,
         typeof(MouseButtonEventHandler),
         _typeofThis);

这就是Popup中发生的事情:

private void OnPreviewMouseButton(MouseButtonEventArgs e)
{
    // We should only react to mouse buttons if we are in an auto close mode (where we have capture)
    if (_cacheValid[(int)CacheBits.CaptureEngaged] && !StaysOpen)
    {
        Debug.Assert( Mouse.Captured == _popupRoot.Value, "_cacheValid[(int)CacheBits.CaptureEngaged] == true but Mouse.Captured != _popupRoot");

        // If we got a mouse press/release and the mouse isn't on the popup (popup root), dismiss.
        // When captured to subtree, source will be the captured element for events outside the popup.
        if (_popupRoot.Value != null && e.OriginalSource == _popupRoot.Value)
        {
            // When we have capture we will get all mouse button up/down messages.
            // We should close if the press was outside.  The MouseButtonEventArgs don't tell whether we get this
            // message because we have capture or if it was legit, so we have to do a hit test.
            if (_popupRoot.Value.InputHitTest(e.GetPosition(_popupRoot.Value)) == null)
            {
                // The hit test didn't find any element; that means the click happened outside the popup.
                SetCurrentValueInternal(IsOpenProperty, BooleanBoxes.FalseBox);
            }
        }
    }
}

因此它被设计为以这种方式工作,您可能不应该使用OnPreviewMouseDown 来完成您在此处尝试完成的任何事情。

【讨论】:

  • 首先感谢您的精彩解释。我意识到弹出窗口需要捕捉该事件,但是我希望有人能有一种技术来克服它所造成的困难。也许有一种方法可以人为地路由事件?我很乐意放弃使用 OnPreviewLeftMouseDown,但是“Slider”控件和实际上相当多的标准 WPF 控件会听它,因此会受到弹出窗口的影响。我可以继续实现我自己版本的“滑块”和其他控件,我只是希望我可以避免这种情况。
  • 据我所知(在快速谷歌搜索之后),不可能改变RoutedEvents 的行为。但是,我不会实施自定义Slider,而是实施自定义Popup,因为这似乎是您的问题的根源并“覆盖”事件/实施。也许你也可以尝试做一个AttachedEvent,但这只是猜测,因为到目前为止我只将它用于属性。
【解决方案2】:

在我的应用程序中,当用户尝试单击主窗口上的滑块时,当弹出控件打开时,弹出控件会窃取鼠标按下事件

虽然您的描述并不完全正确,但这是任何Popup 控件的正常行为。发生这种情况的原因是因为Popup 控件具有焦点,因此它正在侦听Click 事件即使它发生在Popup 的范围之外。现在从逻辑上考虑一下……如果它不这样做,它怎么知道何时关闭?您会在 ComboBox 中使用的 Popup 控件中找到相同的行为。

【讨论】:

  • 你说的完全正确,在我的描述中我试图强调我想让它工作的方式。
【解决方案3】:

有一种解决方法可以实现您需要的行为,为您使用的 Slider 控件设置“IsHitTestVisible = True”。

PS: 设置 IsHitTestVisible = True,仅当 Popup 打开时 - 否则为 False。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多