4

I am trying to get my SelectedRadioButton from a DataTemplate.

Wpf Inspector showed the Visual Tree:

enter image description here

and in code:

    void menu_StatusGeneratorChanged(object sender, EventArgs e)
            {
                var status = Menu.Items.ItemContainerGenerator.Status;
                if (status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
                {
                    var item = Menu.Items.ItemContainerGenerator.ContainerFromIndex(0);
                    // item is a ContentPresenter
                    var control = Tools.FindChild<SelectedRadioButton>(item);
                    control = Tools.FindAncestor<SelectedRadioButton>(item);
                }
            }

item is a ContentPresenter, see the image of Wpf inspector, I believe from there I must be able to get to the SelectedRadioButton. The variable control is always null.
What am I missing here? I use these visualtreehelpers.

Gerard
  • 13,023
  • 14
  • 72
  • 125
  • 1
    It is **extremely uncommon** that you manipulate UI elements like this in WPF. What are you trying to do? – Federico Berasategui Oct 22 '13 at 16:31
  • Set a breakpoint inside that method and use WPF Tree Virtualizer to see the VisualTree at the point of time where debugger hits breakpoint. The complete VisualTree might not be loaded completely and so will WPF Tree Vitualizer show you also null. In case the Virtualizer shows you concrete instance you might be using FindChild wrong. Check out WPF Tree Virtualizer http://blogs.msdn.com/b/zainnab/archive/2010/01/29/using-the-wpf-tree-visualizer-vstipdebug0004.aspx You might be that unlucky and got into worst case where generator is at the end but no measure of the container was executed yet. – dev hedgehog Oct 22 '13 at 16:44
  • @HighCore I want to set a property of one of my custom controls for initialization. However they are generated via a DataTemplate in an ItemsControl and I can find no point of access. – Gerard Oct 22 '13 at 17:20
  • @Gerard seems like that should be done via DataBinding instead. – Federico Berasategui Oct 22 '13 at 17:23
  • @Gerard ;) have you tried it out? – dev hedgehog Oct 22 '13 at 17:30
  • I wouldn't know how to check whether a measure was executed or not. I will have to figure out a way to initialize one item via databinding. I saw that Dr. Wpf discusses this issue in "Finding Template Elements by Mapping Items to Containers" at http://drwpf.com/blog/2008/07/20/itemscontrol-g-is-for-generator/ – Gerard Oct 22 '13 at 19:21
  • 2
    Ok yes that is a good tutorial. I didnt know about that one. Dr wpf calls ApplyTemplate(); that loads and inits inner controls. I approve that tutorial. hehe – dev hedgehog Oct 22 '13 at 23:20

1 Answers1

7

The code that I used to traverse the Visual Tree did not use the ApplyTemplate() method for a FrameworkElement in the tree and therefore cildren could not be found. In my situation the following code works:

    /// <summary>
    /// Looks for a child control within a parent by name
    /// </summary>
    public static DependencyObject FindChild(DependencyObject parent, string name)
    {
        // confirm parent and name are valid.
        if (parent == null || string.IsNullOrEmpty(name)) return null;

        if (parent is FrameworkElement && (parent as FrameworkElement).Name == name) return parent;

        DependencyObject result = null;

        if (parent is FrameworkElement) (parent as FrameworkElement).ApplyTemplate();

        int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < childrenCount; i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            result = FindChild(child, name);
            if (result != null) break;
        }

        return result;
    }

    /// <summary>
    /// Looks for a child control within a parent by type
    /// </summary>
    public static T FindChild<T>(DependencyObject parent)
        where T : DependencyObject
    {
        // confirm parent is valid.
        if (parent == null) return null;
        if (parent is T) return parent as T;

        DependencyObject foundChild = null;

        if (parent is FrameworkElement) (parent as FrameworkElement).ApplyTemplate();

        int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < childrenCount; i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            foundChild = FindChild<T>(child);
            if (foundChild != null) break;
        }

        return foundChild as T;
    }

Thanks for the comments of "dev hedgehog" for pointing that out (I missed it).
I will not use this approach in production code, it has to be done with databinding like "HighCore" commented.

Gerard
  • 13,023
  • 14
  • 72
  • 125