Mouse hit lost on popup menu transparent background in xaml - wpf

I have run into a very strange issue today:
I have a popup menu that opens when I hover above an image. The popup menu is positioned right of the image.
this popup menu is templated to look like a rectangle with a little arrow pointing to the image
the menu contains clickable elements, so I need to be able to move the mouse from the image to the menu without it closing in between, and this is where I get confused: if I move the mouse right over the tip of the arrow and then the menu, everything works fine. If I move the mouse over the transparent space between the image and the menu (above or under the arrow), the menu disappears.
Here is the template for the menu:
<Popup AllowsTransparency="True" Name="c_popup" Width="300" PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}" Placement="Right">
<Grid Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Path Data="M 0 10 L 10 0 10 20 Z" Stroke="#252C37" Fill="#252C37" Margin="0,18,0,0" Grid.Column="0" />
<Grid Grid.Column="1" Background="#252C37">
<TextBlock Text="{Binding Name}" Margin="20,10,0,0" FontSize="18" Foreground="White" HorizontalAlignment="Stretch" VerticalAlignment="Top" TextWrapping="WrapWithOverflow" />
</Grid>
</Grid>
</Popup>
you'll notice that I specifically put Background="Transparent" and not Null (I am aware of the effect this as on the hitTesting, see this question)
funny thing is, though: If I put Background="#01000000", it works as expected. (And I get almost what I want, since this is almost a transparent background)
but I would still like to know what is going on there...
I suspect it has something to do with the fact that this is a popupmenu. I assume WPF does something to remove the hitTesting on any surface that is transparent in a popup menu, either when the background is null (which is expected) or even if it is specifically set to transparent (not expected). Can anyone confirm this?

Converting my comment to an answer.
This seems to be more of a "Windows OS" issue than WPF specifically in the way AllowTransparency="True" at a Window level makes the system ignore the element for mouseover events when Background="Transparent" or Opacity="0" on the element..
A detailed explanation of this behavior can be found Here
Adding the MSDN answer just in case the link goes missing later:
Windows supports transparency at the HWND level through a feature called "layered windows".
Layered windows are represented by a bitmap.
The Operation System(OS) renders the bitmap whenever it needs to.
For the OS need to respond very quickly to mouse movement, Windows uses a dedicated Raw Input Thread (RIT).
RIT runs in the kernel and handles the singals from mouse hardware.
RIT can scan over the HWND hierarchy to detect which window the mouse is over.
For normal windows, the RIT checks the mouse position against the window's rectangle.
For layered windows, the RIT looks in the bitmap that specifies the content for the window and checks the effective transparency at that location. The effective transparency can be affected by opacity, color and alpha channel.
If the pixel is 100% transparent, the RIT skips the window and keeps looking. This point is the reason that MouseMove event is not fired.
If the Background of window and control are both Transparent, all the pixels that represent this control(Canvas in your example) in the layered windows are totally transparent. As described above, RIT will skip them and never send WM_MOUSEMOVE message.
So if you want only show up the control only when the mouse is over and otherwise and the window is transparent, you have to make the control not 100% transparent.
Something to note in this case is, even though Popup is within a Window that does not have AllowTransparency="True", Popup generates a Chrome-less window itself and the AllowTransparency="True" on the Popup affect this new chromeless-Window the same way as mentioned in the MSDN link above.
If this control were just a standard control like Button/Rectangle... then just setting the Background="Transparent" would make it work fine and not have this issue.

Related

How to Prompt Window on Image MouseDown

I'm creating an application (for a tablet PC) where a user will fill out an inspection form. As a final step, the director must be able to sign it (along with a few other people).
Since I want to keep my form small and concise, I was looking to create a click event on an image control which would pop up my signature canvas window.
XAML Code for my Image control
...
<DockPanel HorizontalAlignment="Stretch" Name="dpDirectorImg" VerticalAlignment="Stretch" Grid.Column="1" Grid.Row="8">
<Image Height="Auto" Name="imgDirectorSignature" Stretch="Uniform" Width="Auto" MouseDown="imgDirectorSignature_MouseDown" />
</DockPanel>
...
VB Code
Private Sub imgDirectorSignature_MouseDown(sender As Object, e As System.Windows.Input.MouseButtonEventArgs) Handles imgDirectorSignature.MouseDown
MsgBox("Hello World")
End Sub
For example, in the following screenshot the user would touch / click in the red rectangle (which contains an Image control):
Which would prompt this window:
My problem: I cannot seem to trigger the event on the Image. I have tried TouchDown, MouseDown, StylusDown, GotFocus (with property Focasable set to true) and nothing seems to trigger it.
Well now that we found out what was the problem.
How about you stop using empty images for the future and instead you use Rectangle:
<DockPanel HorizontalAlignment="Stretch" Name="dpDirectorImg" VerticalAlignment="Stretch" Grid.Column="1" Grid.Row="8">
<Rectangle MouseDown="imgDirectorSignature_MouseDown" />
</DockPanel>
If the rectangle has any background color by default you can change it to transparent
Background = Transparent
Glad I was able to help you out on this and notice, when Background or Source of an Image is NULL, the hit testing in wpf will not work. That is by design.
Set Background to transparent or any color you like :)

WPF - Clickable content behind a scrollviewer

Is it possible to have content behind a scrollviewer that still reacts to user mouse input?
<Grid>
<Button Width="50" Height="50"/>
<ScrollViewer Background="{x:Null}"/>
</Grid>
I've tried combinations of zindexes and null backgrounds, but can't seem to stop the scrollviewer from not tunneling the events down.
To prevent eating clicks, make your scroll viewer non-focusable:
<ScrollViewer Focusable="False" />
The scrollviewer is eating click messages. You don't want to put things behind it.
It would be better to put things inside the scrollviewer. You can make a grid that contains the content and a usercontrol behind the content. The control can be themed to be transparent using a rectangle painted with the color "Transparent". The control would still be clickable, and would still fill up all the space within the scrolled content.

Adding a border to image control prevents image from displaying WPF

I have a perplexing problem that makes no sense to me - I am trying to place a border round an image control in WPF. The image control displays an image perfectly (I have loaded through code behind and XAML and both work fine). However when I place a border around the image control the image does not appear at all. This happening with three image controls all with identical config. Does anyone know why this is or how I can fix it? Many thanks, Jeff.
XAML (with border commented out) is below:
<!--<Border BorderBrush="Black" BorderThickness="2" Margin="201,172,618,450" Grid.Column="1">-->
<Image Name="imgFault11" Stretch="Fill" Grid.Column="1" Margin="200,172,619,450">
<!--</Border>-->
You are putting both in Grid.Column="1"
Put the image in the border
Start with no margins
<Border BorderBrush="Black" BorderThickness="2" Grid.Column="1">
<Image Name="imgFault11" Stretch="Fill">
</Border>
You may have to send the border to the Background. Once you add this it overlays the image. Right click the border .. go to order and then choose "Send to Back". The border control is in the toolbox. This is all a little messy though as getting the border to match the image box size takes time to get right and then you have one control on top of another..etc.

SurfaceScrollViewer: getting touch on nested children

I have the following piece of code, which is part of a WPF 4 UserControl:
<Popup x:Name="CantoPopup" IsOpen="False" PlacementRectangle="50,-100,500,120"
AllowsTransparency="True" PopupAnimation="Fade"
StaysOpen="True" Width="500" Height="120">
<Border BorderBrush="#FF120403" BorderThickness="1" CornerRadius="10" Background="#FF9350">
<s:SurfaceScrollViewer x:Name="IndexScroller" Width="500" Height="120" Margin="10" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Visible">
<DockPanel x:Name="InnerIndexPanel" />
</s:SurfaceScrollViewer>
</Border>
</Popup>
The DockPanel is then populated in the code-behind with a collection of TextBlocks. Basically, I am trying to build a scrollable horizontal list of touchable items.
Now, I would like to detect which textblock was touched by the user. However, neither adding a TouchDown event handler to the TextBlocks nor using TouchExtensions to handle tap gestures worked. Can someone please point me in the right direction?
Under the covers, Popup creates another hwnd to render its content into. This is different from all other WPF controls. You need to register this hwnd with the Surface SDK so it will start sending touch events to it. Use this to do that: http://msdn.microsoft.com/en-us/library/microsoft.surface.presentation.input.touchextensions.enablesurfaceinput.aspx
I found out that the point is that the Popup component has some peculiarities :) In this case, it seems that it did not detect neither TouchDown events nor PreviewTouchDown ones. Therefore, I resorted to creating a UserControl made of a Canvas containing the code above and then making such control visible on top of the rest whenever I needed the popup to open. I do not know whether this is the best solution, but now it reacts as expected.

How to make a windows slide out using WPF( expression Blend 4and C#)

I am trying to figure out how to connect windows so the slide out using a small button from the left or right of a main window. What I am trying to do is create a main form with 2 windows connected to it. For one of the windows when the user presses a button on the main window it makes the window seem to slide out rather than pop up. Here is where I got the idea http://www.youtube.com/watch?v=CVlSj0yr3rg&feature=related ..The user then changes a value and the main windows is updated with new information. Honestly I have finished writing all my code and got everything working in Windows Forms in visual studio 2010 (with pop up windows).But I am thinking to make a more appealing gui WPF is the way to go, plus I like learning about it. If you have any forums, tutorials or general answers that would be great.
OK, so judging from the video you really want some kind of expander that opens and not a Window. A Window is an area with border, and the standard buttons and titlebar at the top.
This can be done with a grid with two columns. One is set to Auto width, one is set to * width. In the Auto sized one you can put your expanding content, and have your always visible content in the other.
The simple way to do this:
The Xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StackOverflow"
x:Class="MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="7"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid x:Name="ExpandoGrid"/>
<Button Content="..." Grid.Column="0" HorizontalAlignment="Right" Margin="0" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5" Width="6" Height="40" Click="Button_Click"/>
<Grid x:Name="OtherContentGrid" Grid.Column="1" HorizontalAlignment="Left" Height="100" Margin="-7,0,0,0" VerticalAlignment="Top" Width="100"/>
</Grid>
</Window>
The Code-behind
Imports System.Collections.ObjectModel
Class MainWindow
Public Sub New()
InitializeComponent()
End Sub
Private Sub Button_Click(ByVal sender as Object, ByVal e as System.Windows.RoutedEventArgs)
If Me.ExpandoGrid.Width = 7 Then Me.ExpandoGrid.Width = 200 Else Me.ExpandoGrid.Width = 7
End Sub
End Class
This is by no means the complete way, or the best way. It is one of the simplest to implement though. A better way would be with a ViewModel which would handle the state of the expanded area, along with some animations to make it a smooth transition. If you want the sliding behaviour that is done in that video, animations are where it is at. If you are using Blend, then you have the right tool for animations.
Personally I would have this Windows ViewModel have a property (lets call it DrawerExpanded as Boolean) that a customized Expander would bind its IsExpanded property to. I would then create an open animation that sets the width of the content in the expander, and a close animation that sets the width to 0. Additionally, in each of these I would probably include setting the visibility and opacity to make the effect better and not weird. So lets say expand animation sets Width to 350 at .5 seconds, Visibility to visible at .5 seconds, and then opacity from 0 to 100 from .5 seconds to .7 seconds. That way the drawer slides out and the content fades quickly into view.
If you want a code example of that, you may have to give me a few mins.
I would really just take the easy/friendly route of creating Visual States in Expression Blend. There's basically just an "in state" and "out state", and an InteractionTrigger that allows the control to trigger the state change. Its awesome and extremely user friendly.
No code behind :) Hope it helps you!
As a bonus, you can easily add transition effects just like in a powerpoint. The xaml code gets pretty verbose, but working in Blend allows you to use the IDE to manage everything you add visually.
You can even use the Interaction Trigger to toggle between visibility states of the other controls, rather than writing converters, etc.

Resources