Search code examples
c#wpfstyleshierarchicaldatatemplate

WPF GetTemplateChild get Nullrefrence exception


Hi I'm building a control I need to access checkbox in the code behind But I get the null error

this is my control

<Style TargetType="TreeViewItem" x:Key="CheckTreeViewItem" >
        <Setter Property="IsExpanded" Value="{Binding Path=IsExpanded,Mode=TwoWay}"/>
    </Style>

    <Style TargetType="local:CheckTreeView">
        <Setter Property="ItemContainerStyle" Value="{StaticResource CheckTreeViewItem}"/>
        <Setter Property="ItemTemplate">
            <Setter.Value>
                <HierarchicalDataTemplate DataType="{x:Type local:CheckTreeSource}" ItemsSource="{Binding Children}">
                    <CheckBox x:Name="PART_CheckBox" Margin="1" IsChecked="{Binding IsChecked}">
                        <TextBlock Text="{Binding Text}"/>
                    </CheckBox>
                </HierarchicalDataTemplate>
            </Setter.Value>
        </Setter>
    </Style>

And this is how I access control

[TemplatePart(Name = CheckBox_Key, Type = typeof(CheckBox))]

    public partial class CheckTreeView : TreeView
    {
        private const string CheckBox_Key = "PART_CheckBox";
        CheckBox checkBox;

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            checkBox = GetTemplateChild(CheckBox_Key) as CheckBox;
            checkBox.Click += CheckBox_Click;
        }

        private void CheckBox_Click(object sender, RoutedEventArgs e)
        {

        }
    }

When I use the following code I get no null error but no control in runtime

static CheckTreeView()
            {
                DefaultStyleKeyProperty.OverrideMetadata(typeof(CheckTreeView),
                       new FrameworkPropertyMetadata(typeof(CheckTreeView)));
            }

Solution

  • To subscribe to Click event of each CheckBox:

        public partial class CheckTreeView : TreeView
        {
            public CheckTreeView()
            {
                InitializeComponent();
                processNode(this);
            }
    
    
            void processNode(ItemsControl node)
            {
                node.ItemContainerGenerator.StatusChanged += (sender, args) =>
                {
                    ItemContainerGenerator itemContainerGenerator = ((ItemContainerGenerator)sender);
                    if (itemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
                    {
                        for (int i = 0; i < itemContainerGenerator.Items.Count; i++)
                        {
                            TreeViewItem treeViewItem =
                                (TreeViewItem) itemContainerGenerator.ContainerFromIndex(i);
    
                            treeViewItem.Loaded += (o, eventArgs) =>
                            {
                                CheckBox checkBox = FindVisualChild<CheckBox>(treeViewItem);
                                checkBox.Click += CheckBox_Click;
                            };
    
                            processNode(treeViewItem);
                        }
                    }
                };
            }
    
            public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
            {
                if (obj != null)
                {
                    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
                    {
                        var child = VisualTreeHelper.GetChild(obj, i);
                        if (child is T)
                        {
                            return (T)child;
                        }
    
                        T childItem = FindVisualChild<T>(child);
                        if (childItem != null) return childItem;
                    }
                }
                return null;
            }
    
            private void CheckBox_Click(object sender, RoutedEventArgs e)
            {
    
            }
        }
    

    Note that if you use ObservableCollection as items source, you should process changes in that collection: subscribe to events of added items, unsubscribe from events of removed items.