Question

Is there a way to get the parent (ListView) for a GridViewColumn?

I have tried with LogicalTreeHelper and VisualTreeHelper but no dice.

I can share a what-have-you-tried that is a bit funny, it works but ugly is not close to describing it:

public class Prototype
{
    [Test, RequiresSTA]
    public void HackGetParent()
    {
        var lw = new ListView();
        var view = new GridView();
        var gvc = new GridViewColumn();
        view.Columns.Add(gvc);
        lw.View = view;
        var ancestor = new Ancestor<ListView>();

        var binding = new Binding
        {
            RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(ListView), 1),
            Converter = new GetAncestorConverter<ListView>(), // Use converter to hack out the parent
            ConverterParameter = ancestor // conveterparameter used to return the parent
        };
        BindingOperations.SetBinding(gvc, GridViewColumn.WidthProperty, binding);

        lw.Items.Add(DateTime.Now); // think it cannot be empty for resolve to work
        ResolveBinding(lw);
        Assert.AreEqual(lw, ancestor.Instance);
    }

    private void ResolveBinding(FrameworkElement element)
    {
        element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
        element.Arrange(new Rect(element.DesiredSize));
        element.UpdateLayout();
    }
}
public class GetAncestorConverter<T> : IValueConverter
{
    public object Convert(object value, Type type, object parameter, CultureInfo culture)
    {
        var ancestor = (Ancestor<T>)parameter;
        ancestor.Instance = (T)value;
        return null;
    }

    public object ConvertBack(object o, Type type, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}
public class Ancestor<T>
{
    public T Instance { get; set; }
}
Was it helpful?

Solution

What you want is unfortunately hidden behind an internal property of the DependencyObject InheritanceContext so the only way to access it is via reflection. So if you are comfortable with that then this solution will work.

public class Prototype
{
    [Test, RequiresSTA]
    public void HackReflectionGetParent()
    {
        var lw = new ListView();
        var view = new GridView();
        var gvc = new GridViewColumn();
        view.Columns.Add(gvc);
        lw.View = view;

        var resolvedLw = gvc.GetParents().OfType<ListView>().FirstOrDefault();
        Assert.AreEqual(lw, resolvedLw);
    }
}

public static class DependencyObjectExtensions
{
    private static readonly PropertyInfo InheritanceContextProp = typeof (DependencyObject).GetProperty("InheritanceContext", BindingFlags.NonPublic | BindingFlags.Instance);

    public static IEnumerable<DependencyObject> GetParents(this DependencyObject child)
    {
        while (child != null)
        {
            var parent = LogicalTreeHelper.GetParent(child);
            if (parent == null)
            {
                if (child is FrameworkElement)
                {
                    parent = VisualTreeHelper.GetParent(child);
                }
                if (parent == null && child is ContentElement)
                {
                    parent = ContentOperations.GetParent((ContentElement) child);
                }
                if (parent == null)
                {
                    parent = InheritanceContextProp.GetValue(child, null) as DependencyObject;
                }
            }
            child = parent;
            yield return parent;
        }
    }
}

There was some discussion about this being made public back in 2009 and nothing has happened so I doubt it will be. That said the property is used extensively within the framework and is Friend Visible to other framework assemblies so I think it is a pretty safe bet it isn't going to change soon either.

OTHER TIPS

I know this was asked 3 years ago but I've needed to do this a few times now, and I always keep coming across this answer, so I reckon other people with this issue will have the same problem.

I found an easy solution to this and similar problems provided you're willing to add a bit of code when the listview or gridview's parent object is instantiated.

Long story short: I leverage the .NET System.Collections.Generic.Dictionary functionality in that I create such a Dictionary(Of "object type I am notified of", "object type I need to retrieve"), e.g. Dictionary (Of GridViewColumn, ListView) - and load it at the instantiation of the parent object. If I then, say, get a GridViewColumn and I need to know what its "parent" listview is, I simply refer to this Dictionary to get it.

Seems to to the job for me :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top