How can you set IsHitTestVisible to True on a child of something where it's set to false?
Asked Answered
A

3

12

I have a message which is within a border and is displayed in front of a MDI client area. Because it's just a message, I set it's IsHitTestVisible to false. For convenience, I've also included a button within it so the user can simply click the button to start a new diagram.

However, because the border has its IsHitTestVisible set to False, it shorts out the True value of the button so the user can't click on the button.

That said, how can you make a control invisible to the mouse, while still allowing one of its children to receive mouse events?

Here's a screenshot:

enter image description here

We want the blue area with text to be hit-test invisible, but we want the button to still be able to be pressed. Thoughts? (And yes, I already know about stacking the two items in a grid, but that messes with the layout.

Ashanti answered 27/8, 2012 at 19:25 Comment(3)
I think you'll have more luck figuring out how to fix your layout using a Grid with two controls stacked on each other than you will at finding a way to set IsHitTestVisible=true but allow hit tests anyways on a child item. What sort of layout do you have?Heterochromosome
You could override HitTestCore...to determine the hit behaviour with your UIElement (that's presuming you have created a UserControl)......but like Rachel says...use Grid, or different layout.Bayly
Yeah, I already have a much-lighter-weight control called a LayerPanel that simply stacks things on top of each other. Cuts out all the measurements and such from the Grid, which is notoriously poor at performance. Still, I hate to have to layer things, because then when the text changes for localization, it pushes the layout and now you have to worry about aligning something in front of something unconnected. Not fun. Doable, but not fun.Ashanti
A
2

Ok, doesn't look like the question can be solved as asked, and therefore it looks like we have to revert to the layered method. In that case, to compensate for text changes, you have to set the vertical alignment of the button to Bottom, and ensure there's extra padding on the bottom edge of the border or bottom margin on the TextBlock) to ensure the label always floats above the button. You manage the side margins/padding for both using the same principles.

This will give us what we're after, but just seems pretty cumbersome. Again, I wish there was some way to specify IsHitTestVisible (or IsEnabled for that matter) with a 'Force' somehow that re-establishes them (and below) as the owner of the behaviors. A person can dream. Until then... layers it is!

Ashanti answered 9/1, 2013 at 7:40 Comment(0)
T
4

DISCLAIMER: It looks it wont solve exact problem that OP has but i will keep it here because it might be helpful for others with similar issue.

Clicking on grid should not register any mouse events on grid.

<Grid Background="{x:Null}">
   <Button/>
<Grid/>

EDIT: When you want background to be visibile then i would suggest this: For demostration i set cursor="hand" on border

<Canvas>
    <Border Height="300" Width="300" Background="Red" Cursor="Hand" IsHitTestVisible="False"/>
    <Button Content="click"/>
</Canvas>

or

<Grid>
    <Border  Background="Red" Cursor="Hand" IsHitTestVisible="False" />
    <Button Content="click" HorizontalAlignment="Center"/>
</Grid>

It both cases the trick is that the control (button) is not child of parent with IsHitTestVisible="False" and it is just overlaping the background control

Threadfin answered 18/5, 2019 at 18:37 Comment(3)
That makes the grid invisible. I only want it invisible to the mouse, not the user.Ashanti
Yes, as I mentioned in my original question, I'm already familiar with this technique, but since the button is not within the border, you have to do extra calculations to figure out how to 'pad' the button to make it look like it's still within the border. But I guess that is the only way to do it.Ashanti
This actually helped me a lot with a similar issue. I thought that setting background to transparent or x:Null was the same but after trying it, it turns out that it is not the same. For me it was about having a panel that I could position clickable elements on, but clicks that did not hit a child element still went through to what was behind. This solution worked for that.Sama
A
2

Ok, doesn't look like the question can be solved as asked, and therefore it looks like we have to revert to the layered method. In that case, to compensate for text changes, you have to set the vertical alignment of the button to Bottom, and ensure there's extra padding on the bottom edge of the border or bottom margin on the TextBlock) to ensure the label always floats above the button. You manage the side margins/padding for both using the same principles.

This will give us what we're after, but just seems pretty cumbersome. Again, I wish there was some way to specify IsHitTestVisible (or IsEnabled for that matter) with a 'Force' somehow that re-establishes them (and below) as the owner of the behaviors. A person can dream. Until then... layers it is!

Ashanti answered 9/1, 2013 at 7:40 Comment(0)
M
1

AFAIK you can't. you can do it however with IsEnabledProperty, and still with a little workaround. you have to create your own button like this:

Edited 9/1/2013 :

 public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        DataContext = this;
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("hello");
    }
}

public class MyButton : Button
{
    static MyButton()
    {
        IsEnabledProperty.OverrideMetadata(typeof(MyButton),
        new UIPropertyMetadata(true,(o, args) => { },(o, value) => value));
    }
}

XAML:

<BorderIsEnabled="False">
    <wpfApplication2:MyButton Margin="61,95,88,100" Click="ButtonBase_OnClick">Open a new diagram</wpfApplication2:MyButton>
</Border>
Ministry answered 8/1, 2013 at 16:44 Comment(3)
Not really following what exactly you're trying to show here. You seem to have defined both PropertyChanged and Coercion anonymous delegates but both just do the default behavior, so why specify them at all? And for that matter, you set the default value to True, which is already the default for IsEnabled, which begs the question why are you overriding the metadata in the first place? Again, not really following what you're trying to achieve with this since it effectively appears to not change anything.Ashanti
The default behavior of the IsEnabledProperty is to cascade its value from the container to its children, once you overrides and you set in the parent the IsEnabled=False it will not effect the child.Ministry
Um... but couldn't you then just set the IsEnabled in the child to True without having to subclass anything? I mean if as you said the default behavior of the IsEnabled is to be inherited by its children, then all you should have to do is set IsEnabled on the child directly, without the subclass, no?Ashanti

© 2022 - 2024 — McMap. All rights reserved.