TTGroup
TTGroup

Reputation: 3703

Can not find child control on ItemContainerGenerator?

I'm using ItemsControl for StackPanel as below code:

File playBackControl.xaml - Begin

    <ScrollViewer x:Name="scrollViewerChannelBtns">
        <StackPanel x:Name="channelBtns" Orientation="Vertical" MouseWheel="ScrollViewer_MouseWheel">
            <ItemsControl x:Name="channelBtnItems" ItemsSource="{Binding}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <ToogleButton x:Name="tgbChannelName"
                                  HorizontalAlignment="Center" VerticalAlignment="Center"
                                  Width="{Binding Path=ChannelNameBtnWidth}" 
                                  Height="{Binding Path=ChannelNameBtnHeight}"
                                  Margin="{Binding Path=ChannelNameBtnMargin}"
                                  IsChecked="{Binding Path=IsChecked, Mode=TwoWay}"
                                  ToolTip="{Binding Path=ToolTip}" Tag="{Binding Path=Index}"
                                  IsEnabled="{Binding Path=IsEnabled}" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
    </ScrollViewer>

 File playBackControl.xaml - End


 File playBackControl.xaml.cs - Begin

public partial class PlayBackControl : UserControl
{
    public static List<ChannelBtnItemData> listChannelBtnItemData = new List<ChannelBtnItemData>();
    public PlayBackControl() //This will run first when the app start
    {
        InitializeComponent();
        channelBtnItems.ItemContainerGenerator.StatusChanged += ChannelBtnItemsStatusChangedEventHandler;
        System.Threading.Thread threadTimer = new System.Threading.Thread(TimerThreadThreadProc);
        threadTimer.Start();
    }

    private void TimerThreadThreadProc()
    {
        while (true)
        {
            Thread.Sleep(60000);     //Sleep 60s                    
            this.Dispatcher.BeginInvoke(new Action(delegate()
            {
                //Re init listChannelBtnItemData, this list has about 64 items
                channelBtnItems.ItemsSource = listChannelBtnItemData;
                channelBtnItems.Items.Refresh();
            }));
        }
    }

    public class ChannelBtnItemData : INotifyPropertyChanged
    {
        private String _toolTip;
        private int _index;
        private int _channelID;
        private bool _isChecked;
        private bool _isEnabled;
        private bool _lockToggle;
        private double _channelNameBtnWidth;
        private double _channelNameBtnHeight;
        private Thickness _channelNameBtnMargin;

        public String ToolTip
        {
            get { return _toolTip; }
            set
            {
                _toolTip = value;
                OnPropertyChanged("ToolTip");
            }
        }

        public int Index
        {
            get { return _index; }
            set
            {
                _index = value;
                OnPropertyChanged("Index");
            }
        }

        public int ChannelID
        {
            get { return _channelID; }
            set
            {
                _channelID = value;
                OnPropertyChanged("ChannelID");
            }
        }

        public bool IsChecked
        {
            get { return _isChecked; }
            set
            {
                _isChecked = value;
                OnPropertyChanged("IsChecked");
            }
        }

        public bool IsEnabled
        {
            get { return _isEnabled; }
            set
            {
                _isEnabled = value;
                OnPropertyChanged("IsEnabled");
            }
        }

        public bool LockToggle
        {
            get { return _lockToggle; }
            set
            {
                _lockToggle = value;
                OnPropertyChanged("LockToggle");
            }
        }

        public double ChannelNameBtnWidth
        {
            get { return _channelNameBtnWidth; }
            set
            {
                _channelNameBtnWidth = value;
                OnPropertyChanged("ChannelNameBtnWidth");
            }
        }

        public double ChannelNameBtnHeight
        {
            get { return _channelNameBtnHeight; }
            set
            {
                _channelNameBtnHeight = value;
                OnPropertyChanged("ChannelNameBtnHeight");
            }
        }

        public Thickness ChannelNameBtnMargin
        {
            get { return _channelNameBtnMargin; }
            set
            {
                _channelNameBtnMargin = value;
                OnPropertyChanged("ChannelNameBtnMargin");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }

        public ChannelBtnItemData()
        {

        }
    }

    private void ChannelBtnItemsStatusChangedEventHandler(Object sender, EventArgs e)
    {
        if (channelBtnItems.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
        {
            if (channelBtnItems.HasItems)
            {
                var containers = channelBtnItems.Items.Cast<Object>().Select(
                item => (FrameworkElement)channelBtnItems.ItemContainerGenerator.ContainerFromItem(item));
                foreach (var container in containers)
                {
                    if (container != null)
                        container.Loaded += ChannelBtnItemContainerLoaded;
                }
            }
        }
    }

    private void ChannelBtnItemContainerLoaded(object sender, RoutedEventArgs e)
    {
        var element = (FrameworkElement)sender;
        element.Loaded -= ChannelBtnItemContainerLoaded;

        ToogleButton tgbChannelName = FindChild<ToogleButton>(element, "tgbChannelName");
        if (tgbChannelName != null) //Sometimes It equal null
        {
            //Do something
        }            
    }

    public T FindChild<T>(DependencyObject parent, string childName)
       where T : DependencyObject
    {
        // Confirm parent and childName are valid. 
        if (parent == null) return null;

        T foundChild = null;

        int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < childrenCount; i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            // If the child is not of the request child type child
            T childType = child as T;
            if (childType == null)
            {
                // recursively drill down the tree
                foundChild = FindChild<T>(child, childName);

                // If the child is found, break so we do not overwrite the found child. 
                if (foundChild != null) break;
            }
            else if (!string.IsNullOrEmpty(childName))
            {
                var frameworkElement = child as FrameworkElement;
                // If the child's name is set for search
                if (frameworkElement != null && frameworkElement.Name == childName)
                {
                    // if the child's name is of the request name
                    foundChild = (T)child;
                    break;
                }
                else
                {
                    // recursively drill down the tree
                    foundChild = FindChild<T>(child, childName);

                    // If the child is found, break so we do not overwrite the found child. 
                    if (foundChild != null) break;
                }
            }
            else
            {
                // child element found.
                foundChild = (T)child;
                break;
            }
        }

        return foundChild;
    }
}

File playBackControl.xaml.cs - End

In the ChannelBtnItemContainerLoaded() function, sometimes the tgbChannelName equal null,

I have searched and read more about this, but I don't know how to fix it.

Upvotes: 1

Views: 365

Answers (1)

dev hedgehog
dev hedgehog

Reputation: 8791

I create a demo to test this but it worked for me. Loaded event is being fired when all is there available to access and so I never ran into the state where instance was null.

However I have a feeling that you haven't revealed us what you exactly up to there.

Are you changing the ItemsSource at runtime? At what point do you swap the ItemsSource?

Where is the following code of yours being called?

channelBtnItems.ItemContainerGenerator.StatusChanged += ChannelBtnItemsStatusChangedEventHandler;
channelBtnItems.ItemsSource = listChannelBtnItemData;
channelBtnItems.Items.Refresh();

However here is a trick how you can "postpone" an action.

Use the Dispatcher.BeginInvoke with DispatcherPrority.Background.

http://weblogs.asp.net/pawanmishra/archive/2010/06/06/understanding-dispatcher-in-wpf.aspx

    private void ChannelBtnItemContainerLoaded(object sender, RoutedEventArgs e)
    {
        var element = (FrameworkElement)sender;
        element.Loaded -= ChannelBtnItemContainerLoaded;

        element.Dispatcher.BeginInvoke((Action)(() =>
        {
            ToggleButton tgbChannelName = FindChild<ToggleButton>(element, "tgbChannelName");
            if (tgbChannelName != null) //Sometimes It equal null
            {
                //Do something
            }
            else
            {

            }
        }), DispatcherPriority.Background);
    }

Try it out. If this doesnt help please provide us with the complete code including data and viewmodel so can test on same code just like yours.

Upvotes: 1

Related Questions