Problems with using Controls.Popup as error adorner in WPF - wpf

My (simplified) validation template is
<Grid>
<Border x:Name="ErrorAdorner"
BorderBrush="Red">
<AdornedElementPalceHolder />
</Border>
<Popup x:Name="ErrorPopup"
PalcementTarget="{Binding ElementName=ErrorAdorner}"
Placement="Bottom"
StaysOpen="false"
IsOpen="true">
<Grid>
<TextBloxk Text="Error!!!" />
</Grid>
</Popup>
</Grid>
The adorned element is typically a textbox
The problem I have with this approach is that, as soon as I click inside the textbox, the ErrorPopup disappears and the ErrorAdorner remains visible. Desired behavior is that both should stay visible.
Things tried:
Set StaysOpen to true on ErrorPopup. Problem: when you resize/move the parent window when the error is visible, the ErrorPopup remains at the same location, it doesnt move along with the textbox
Use a StackPanel around the textbox (adorned element) and the error message text block. Problem: Popup comes with positioning capabilities ie., if there is not enough screen area below the textbox for the adorner, it automatically relocates it. But if a stack panel is used, error message just cuts off if there is no space or it changes the textbox layout(not desired)
So in essence, I want to use the popup for its positional capabilities, but somehow want to fix the visibility problem

The problem here is that you can resize the window even if the cursor is inside the TextBox, you cannot get any useful state information out of that, so if you make the IsOpen dependent on that you still get dislocated popups.
Possibly this related question can help you with the placement.

Related

How to expand a control beyond the parent's bounds?

I'm trying to popup a list box when a toggle button is clicked. In the code behind I work with the Visibility property and set it to Visibility.Visible. However, although the list box appears on the screen, it gets cropped to the size of the toggle box (30x30), instead of, as I wish it to, its own size (99x99).
<ToggleButton HorizontalAlignment="Left" Margin="0,0,0,0"
VerticalAlignment="Top" Width="30" Height="30"
Click="ButtonBase_OnClick">
<ListBox x:Name="listBox1"
ClipToBounds="False"
Visibility="Collapsed"
HorizontalAlignment="Left"
Height="99"
VerticalAlignment="Top"
Width="99">
<CheckBox x:Name="checkBox3" Content="CheckBox"/>
<CheckBox x:Name="checkBox4" Content="CheckBox"/>
</ListBox>
</ToggleButton>
I've tried setting the property ClipToBounds to false but it didn't give much change. I also tried setting the height of the combo box to Auto. That made a scroll bar appear (a horizontal one, though, which itself was a bit unexpected) but everything was still cropped to the toggle box's bounds.
What do I need to set more? Or am I approaching it the wrong way (as in: should I define the combo box inside the toggle button's child tags for templates, trigger and what not)?
If you don't want the listbox inside the toggle button, you should not place it inside the toggle button. Place it outside of the toggle button and it should work. Maybe you should have a look at the Expander control, that seems to do what you need. It will still not make the expanders body larger than the expander.
Something else to note: it's bad style to set fixed sizes. Try to partition your window using the containers like Grid and the various Panels and you will not need fixed sizes. If your toggle button does not have a fixed size, it could expand when the contents need expansion.

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.

Mouse hit lost on popup menu transparent background in xaml

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.

Try to catch mouse event in HierarchicalDataTemplate

I'm fairly new to wpf and this website. Please give me some mercy if I show some mistakes.
My HierarchicalDataTemplate ,which is my treeview item, consists of multiple components: two textblocks , image , and checkbox with some stack panels for the layout. My MouseEventHandler, which is to catch when user click on textboxes, image , or checkbox , is TreeViewItem.Selected. But when I click on the tiny space between those components, it doesn't trigger TreeViewItem.Selected.
My first initial thought was that I might need to specify event handler on the stack panel which was for the layout of my HierarchicalDataTemplate . However, it didn't rise the event ,even though I made event handler on stack panel specifically.
Could you give me some guidance?
ps. I used binding for IsSelected property but it never notified to change its property
Set Background="Transparent" for topmost layout container inside your HierarchicalDataTemplate.
The following Grid doesn't raise MouseLeftButtonDown event:
<Grid MouseLeftButtonDown="handler" Width="200" Height="200">
</Grid>
But the following does:
<Grid MouseLeftButtonDown="handler" Width="200" Height="200" Background="Transparent">
</Grid>
This is because in the 1st case it doesn't have background and there's nothing to raise MouseLeftButtonDown event. So the event will be raised only if user clicks on some element inside that Grid.

AdornerDecorator - what does it matter where they are placed?

With some xaml like this:
<Grid Name="grid">
<AdornerDecorator>
<TextBox Height="23" HorizontalAlignment="Left" Name="textBox1" Width="120" />
</AdornerDecorator>
</Grid>
The WPF Snoop utility indicates textBox1 is a child of AdornerDecorator (as you would expect) but also that the AdornerLayer that AdornerDecorator creates is also a child. As a custom adorner added to the AdornerLayer can be displayed 'outside' the textbox, the AdornerLayer's drawing surface must stretch outside too (presumably all over the window).
So, what real significance does the placement of AdornerDecorator have (given we bind a UI element to the custom adorner, which we place in the AdornerLayer)? I know AdornerLayer.GetAdorner(textBox1) will get the first adorner layer in the visual tree up from textbox1, but what does it matter where that is (as the custom ardorner gets added to the layer and the custom ardoner knows which element it is bound to)?
The short answer is that it matters when you start to deal with controls that overlap other controls (in the z-index plane) and you care about whether or not a particular adorner layer shows on top of the overlapping controls. For example, when you use an ErrorTemplate, its contents get rendered in an adorner layer and if you don't supply an <AdornerDecorator> in your app (meaning that you just use the one provided by most Window templates), then you can end up with this happening.
By placing the <AdornerDecorator> where we want, we can control how this overlapping behaves.

Resources