I'm trying to do a drag-n-drop operation from a control inside a Popup
. The content being dragged will be placed at the position of the drop so when the drag starts I want to make the Popup
semitransparent to allow placement under it. But so far I fail to make mouse events pass through my semitransparent Popup
.
The following xaml works fine, the Button
is still clickable when hovering the Rectangle
with the mouse
<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>
But the following xaml fails to allow mouse event to pass through when the mouse hovers the Rectangle
inside the Popup
.
<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>
I've tried to get the PopupRoot
top parent of the Rectangle
and set both IsHitTestVisible
and Opacity
for every element in the visual tree explicitly but it still doesn't work. The only way I can sort of get it to work is by setting Background
/Fill
to Transparent
but it looks weird and hovering the parts that aren't Transparent
still fails.
I've read the following link but as far as I can tell, that will only work for a Window
and not a Popup
.
How to create a semi transparent window in WPF that allows mouse events to pass through
Does anyone have any solutions or suggestions? :-)
Thanks to the comment made by @NETscape I tried to get a hold of the actual Window
for the Popup
. It works with the following code (after the Popup
is initially shown I might add..)
// FromVisual can take any Visual inside the Popup..
HwndSource popupHwndSource = HwndSource.FromVisual(rectangle) as HwndSource;
Combining this with the answer to this question I created an attached behavior which adds the property IsPopupEventTransparent
which can be set on any child in the Popup
. At first, this solution caused a bug which forced the users to click twice to re-activate the Window
after the Popup
lost its event transparency. I solved this with a "Mouse.Capture
-workaround".
elementInPopup.Dispatcher.BeginInvoke(new Action(() =>
{
Keyboard.Focus(elementInPopup);
Mouse.Capture(elementInPopup);
elementInPopup.ReleaseMouseCapture();
}));
Usable like this
<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 based on the answer to this 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();
}));
}
}
}
The issue is not with popup but issue is with the color Green
when applied to the rectangle this effectively blocks the mouse event to pass through
try the sample below with transparent background, I placed a border to show the presence
<Grid>
<Popup Placement="Center"
AllowsTransparency="True"
IsHitTestVisible="False"
IsOpen="True">
<Border Width="100"
Height="50"
Background="Transparent"
BorderBrush="Black"
BorderThickness="4"
IsHitTestVisible="False"
Opacity="0.5" />
</Popup>
<Button Content="Click Me"
FontSize="22"
FontWeight="Bold" />
</Grid>
so any color having non zero alpha
component will block the mouse events
refer to below for alpha example
- #FF00FF00 Green
Fail
- #9900FF00 semi transparent Green
Fail
- #0100FF00 almost invisible Green
Fail
- #0000FF00 full transparent Green
Pass
so you need to have transparent color in order for mouse event to be passed through
Since popup is a generated as a layered window on top of the app window, so if you need a background you may need to implement the how to create the semi transparent window solution. there seems to be no built solution in wpf