c# - 允许鼠标事件通过半透明的 Popup

标签 c# wpf xaml popup wpf-controls

我正在尝试从 Popup 中的控件执行拖放操作。被拖动的内容将放置在放置位置,因此当拖动开始时,我想让 Popup 半透明以允许放置在它下面。但到目前为止,我无法让鼠标事件通过我的半透明 Popup

下面的 xaml 工作正常,当鼠标悬停在 Rectangle 上时,Button 仍然可以点击

<Grid>
    <Rectangle Panel.ZIndex="2"
               Width="200"
               Height="200"
               Fill="Green"
               IsHitTestVisible="False"
               Opacity="0.5"/>
    <Button Content="Click Me"
            FontSize="22"
            FontWeight="Bold"/>
</Grid>

enter image description here

但是当鼠标悬停在 Popup 内的 Rectangle 上时,以下 xaml 无法允许鼠标事件通过。

<Grid>
    <Popup Placement="Center"
           AllowsTransparency="True"
           IsHitTestVisible="False"
           IsOpen="True">
        <Rectangle Width="100"
                   Height="50"
                   Fill="Green"
                   IsHitTestVisible="False"
                   Opacity="0.5"/>
    </Popup>
    <Button Content="Click Me"
            FontSize="22"
            FontWeight="Bold"/>
</Grid>

enter image description here

我尝试获取 RectanglePopupRoot 顶层父级并设置 IsHitTestVisibleOpacity明确地针对可视化树中的每个元素,但它仍然不起作用。我能让它工作的唯一方法是将 Background/Fill 设置为 Transparent 但它看起来很奇怪并且悬停在不透明的部分't Transparent 仍然失败。

我已经阅读了以下链接,但据我所知,它只适用于 Window 而不是 PopupHow to create a semi transparent window in WPF that allows mouse events to pass through

有没有人有任何解决方案或建议? :-)

最佳答案

感谢@NETscape 的评论,我试图获得Popup 的实际Window。它适用于以下代码(在 Popup 最初显示后我可能会添加..)

// FromVisual can take any Visual inside the Popup..
HwndSource popupHwndSource = HwndSource.FromVisual(rectangle) as HwndSource;

将此与 this question 的答案结合起来我创建了一个附加行为,它添加了属性 IsPopupEventTransparent,可以在 Popup 中的任何子项上设置该属性。起初,此解决方案导致了一个错误,该错误迫使用户在 Popup 失去其事件透明度后单击两次以重新激活 Window。我用“Mouse.Capture-workaround”解决了这个问题。

elementInPopup.Dispatcher.BeginInvoke(new Action(() =>
{
    Keyboard.Focus(elementInPopup);
    Mouse.Capture(elementInPopup);
    elementInPopup.ReleaseMouseCapture();
}));

像这样使用

<Popup AllowsTransparency="True"
       ...>
    <Border inf:PopupBehavior.IsPopupEventTransparent="{Binding SomeProperty}"
            BorderThickness="1"
            BorderBrush="Black"
            Background="White"
            Margin="0 0 8 8">
        <Border.Effect>
            <DropShadowEffect ShadowDepth="2.25" 
                              Color="Black"
                              Opacity="0.4"
                              Direction="315"
                              BlurRadius="4"/>
        </Border.Effect>
        <!--...-->
    </Border>
</Popup>

弹出行为

public class PopupBehavior
{
    public static readonly DependencyProperty IsPopupEventTransparentProperty =
        DependencyProperty.RegisterAttached("IsPopupEventTransparent",
                                            typeof(bool),
                                            typeof(PopupBehavior),
                                            new UIPropertyMetadata(false, OnIsPopupEventTransparentPropertyChanged));

    public static bool GetIsPopupEventTransparent(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsPopupEventTransparentProperty);
    }
    public static void SetIsPopupEventTransparent(DependencyObject obj, bool value)
    {
        obj.SetValue(IsPopupEventTransparentProperty, value);
    }
    private static void OnIsPopupEventTransparentPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        FrameworkElement element = target as FrameworkElement;
        if ((bool)e.NewValue == true)
        {
            FrameworkElement topParent = VisualTreeHelpers.GetTopParent(element) as FrameworkElement;
            topParent.Opacity = 0.4;
            HwndSource popupHwndSource = HwndSource.FromVisual(element) as HwndSource;
            WindowHelper.SetWindowExTransparent(popupHwndSource.Handle);
        }
        else
        {
            FrameworkElement topParent = VisualTreeHelpers.GetTopParent(element) as FrameworkElement;
            topParent.Opacity = 1.0;
            HwndSource popupHwndSource = HwndSource.FromVisual(element) as HwndSource;
            WindowHelper.UndoWindowExTransparent(popupHwndSource.Handle, element);
        }
    }
}

WindowHelper 基于对此 this question 的回答

public static class WindowHelper
{
    private static Dictionary<IntPtr, int> _extendedStyleHwndDictionary = new Dictionary<IntPtr, int>();

    const int WS_EX_TRANSPARENT = 0x00000020;
    const int GWL_EXSTYLE = (-20);

    [DllImport("user32.dll")]
    static extern int GetWindowLong(IntPtr hwnd, int index);

    [DllImport("user32.dll")]
    static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);

    public static void SetWindowExTransparent(IntPtr hwnd)
    {
        int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
        SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
        if (_extendedStyleHwndDictionary.Keys.Contains(hwnd) == false)
        {
            _extendedStyleHwndDictionary.Add(hwnd, extendedStyle);
        }
    }

    public static void UndoWindowExTransparent(IntPtr hwnd, FrameworkElement elementInPopup)
    {
        if (_extendedStyleHwndDictionary.Keys.Contains(hwnd) == true)
        {
            int extendedStyle = _extendedStyleHwndDictionary[hwnd];
            SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle);
            // Fix Focus problems that forces the users to click twice to
            // re-activate the window after the Popup loses event transparency
            elementInPopup.Dispatcher.BeginInvoke(new Action(() =>
            {
                Keyboard.Focus(elementInPopup);
                Mouse.Capture(elementInPopup);
                elementInPopup.ReleaseMouseCapture();
            }));
        }
    }
}

关于c# - 允许鼠标事件通过半透明的 Popup,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24570144/

相关文章:

c# - 将 DynamicTableEntity 转换为 Json

c# - 如何合并波段和列?

c# - MVVM:GUI 和 ViewModel 之间的真正分离

wpf - 将IEnumerable <T>与ObservableCollection <T>包装在一起

xaml - XML 命名空间中的未知类型 'AppBarButton' - Windows 8 Store App XAML,添加应用栏时出现问题

silverlight - 使用转义字符串格式进行绑定(bind)会破坏 VS IDE 和智能感知

c# - 显示来自 JSON 文件的组标题列表

c# - C# 中的一般多返回语句问题

c# - .NET Core 相当于 Thread.Abort

c# - 从不同 View 修改HamburgerButtonVisibility属性