views:

328

answers:

2

This is a simplified example.

I have a canvas and a usercontrol inside it.

I set the canvas to IsHitTestVisible = false

How can I tell that the usercontrol is no longer hittestvisible? And preferably get a notification that it changed.

I tried databinding to IsHitTestVisible on the user control but it does not change when I change the value on the canvas.

I have the same problem with opacity and visibility....

A: 

You do have an event IsHitTestVisibleChanged, which is raised when IsHitTestVisible property changes. I don't think there should be an issue with data binding too, as IsHitTestVisible is a dependency property and it will generate change notification.

It would be helpful if you could post exactly how you binded.

Trainee4Life
Are you saying that the binding for IsHitTestVisible should change on the child when the parent is also changed?
zachary
The canvas gets ishittestvisible changed. The user control needs to know if it is hittestvisible. I guess the trick is how can a control know that its parent has had ishittestvisiblechanged without having any reference to the parent? The control is no longer hittestvisible, but how to know that has happened?
zachary
Well, the UserControl can get a reference to its parent via the Parent property. Would that be useful? You could then set up a Binding in code to the IsHitTestVisible property so you get the change notifications.Though this would not work if the Parent's Parent get IsHitTestVisible false (and so on up the tree). Is that a scenario that you need to handle? Or do you only care about the immediate parent?
KeithMahoney
For the example it was the parent, however in the real situation the parent is many levels up and the number of levels can be different for each usercontrol and I need to fire an event to know when the change has happened.
zachary
OK. So do you want to find out whether any of the elements which are directly or indirectly parents of the user control, have there IsHitTestVisible property changed?*Removed that comment where I "guessed" that IsHitTestVisible is inheritable. I guessed it wrong.
Trainee4Life
A: 

So your problem is that you have a UserControl inside of a Canvas. Your UserControl needs to know when the Canvas gets IsHitTestVisible set to false.

IsHitTestVisible is not a property that gets "inherrited" down the tree. If you set IsHitTestVisible=false on an object, that property doesn't change on it children.

So, one approach is that you can create a property on your UserControl, say "IsParentHitTestVisible" and bind that property on your UserControl to the Canvas's IsHitTestVisible property. Then in your UserControl, you can handle the change notifications for the property.

Here is the solution that I tried. First, I created a helper class to make it easier to get the change notifications for a property:

public class PropertyNotifier : UserControl
{
    public static readonly DependencyProperty ThePropertyProperty =
        DependencyProperty.Register("TheProperty", typeof(object), typeof(PropertyNotifier), new PropertyMetadata(OnPropertyChanged));

    private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        PropertyNotifier obj = (PropertyNotifier)d;
        if (obj.PropertyChanged != null)
        {
            obj.PropertyChanged(obj, null);
        }
    }

    public event EventHandler PropertyChanged;

    public void Monitor(string prop, Object instance)
    {
        Binding binding = new Binding();
        binding.Source = instance;
        binding.Path = new PropertyPath(prop);
        SetBinding(ThePropertyProperty, binding);
    }

    public void StopMonitoring()
    {
        SetValue(ThePropertyProperty, null);
    }
}

This just lets you monitor a property on an object and get an event when it changes. So you can do something like:

PropertyNotifier pn = new PropertyNotifier();
pn.Monitor("IsHitTestVisible", theCanvas);
pn.PropertyChanged += new EventHandler(pn_PropertyChanged);

and the PropertyChanged event will be fired if IsHitTestVisible changes on "theCanvas".

So, I set up the Loaded event in the UserControl to look like:

    private List<PropertyNotifier> notifiers = new List<PropertyNotifier>();
    void MyUserControl_Loaded(object sender, RoutedEventArgs e)
    {
        foreach (var notifier in notifiers)
        {
            notifier.PropertyChanged -= pn_PropertyChanged;
            notifier.StopMonitoring();
        }
        notifiers.Clear();

        foreach (var parent in GetAllParents(this))
        {
            PropertyNotifier pn = new PropertyNotifier();
            pn.Monitor("IsHitTestVisible", parent);
            pn.PropertyChanged += new EventHandler(pn_PropertyChanged);
            notifiers.Add(pn);
        }
    }

    static IEnumerable<FrameworkElement> GetAllParents(FrameworkElement element)
    {
        FrameworkElement parent = element.Parent as FrameworkElement;
        if (parent != null)
        {
            yield return parent;
            foreach(var el in GetAllParents(parent))
            {
                yield return el;
            }
        }
    }

I don't know if this will work in every situation, so you will need to test it out. There also might be an easier way, but this is the best that I could come up with.

KeithMahoney
The problem I have is in the real world situation there is no easy way for the user control to get any reference to the canvas
zachary
Is the UserControl a direct child of the Canvas? Or is what you are trying to do is determine when any parent all the way up the tree gets this property changed?Ultimately, what are you trying to do? Why does your UserControl need to know if its parent has this property changed?
KeithMahoney
the user control isn't a direct child of the canvas. in the real situation the user control is inside of a window control that I wrote. The window control gets IsHittestvisible and opacity changed when it is minmaxed. In addition the user control can be inside of a tab control inside of the window so I also need to know if the user control is on the active tab. The idea is that the user control will not do any processing when minimized
zachary
It's not too easy to do what you want to do. Try this:In UserControl.Loaded find all of the parents by following up the tree of Parent pointers.On each parent, set up a databinding to the IsHitTestVisible property so that you get notified of the change.There is one extra thing to handle; if any of the parents change (e.g. get moved to a different panel). In this case, I THINK the Loaded event should fire again, so you will want to update the bindings that you are monitoring. Would that work?
KeithMahoney
I updated my answer to include some example code showing what I tried.
KeithMahoney