感谢@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>
PopupBehavior
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();
}));
}
}
}