2

I need to create a new ListBox based on items that are selected (checked). The following code actually worked if I only had like 20 items on the ListBox, but adding more items make it crash. Can anybody know how to make it work, or have a different aproach? Is there a limite for looping through a listBox?

    // worked fine for 20 items,
    // but my actual list contems 95 items...
    private void btnCreateNewList_Click(object sender, RoutedEventArgs e)
    {

                int totalItemsCB = ListCheckBoxVocabulary.Items.Count;
                for (int ii = 0; ii < totalItemsCB-1; ii++)
                {
                    ListBoxItem item = this.ListCheckBoxVocabulary.ItemContainerGenerator.ContainerFromIndex(ii) as ListBoxItem;
                    CheckBox thisCheckBox = FindFirstElementInVisualTree<CheckBox>(item);
                    if (thisCheckBox.IsChecked == true) 
                    {

                        dataPlayListSource.Add(new SampleData() { Text = thisCheckBox.Content.ToString() + " | " + ii });
                        // this.PlayListCheckBoxVocabulary.UpdateLayout();
                        this.PlayListCheckBoxVocabulary.ItemsSource = dataPlayListSource;
                    }

                }
    }

    private T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
    {
        var count = VisualTreeHelper.GetChildrenCount(parentElement);
        if (count == 0)
            return null;

        for (int i = 0; i < count; i++)
        {
            var child = VisualTreeHelper.GetChild(parentElement, i);

            if (child != null && child is T)
            {
                return (T)child;
            }
            else
            {
                var result = FindFirstElementInVisualTree<T>(child);
                if (result != null)
                    return result;

            }
        }
        return null;
    }

and xaml:

        <controls:PivotItem Header="Vocabulary" >
            <ListBox x:Name="ListCheckBoxVocabulary" Margin="0,0,-12,0" ItemsSource="{Binding Items}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <!--<StackPanel Margin="0,0,0,17" Width="432">-->
                        <CheckBox x:Name="cbVocabulary" Content="{Binding Text}" Checked="CheckBox_Checked" Unchecked="UncheckBox" />
                        <!--</StackPanel>-->
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </controls:PivotItem>
Robaticus
  • 22,857
  • 5
  • 54
  • 63
  • what is the exception you are getting? – earthling Mar 27 '12 at 21:52
  • Hi,,, I am getting the following: Reference is not a valid visual DependencyObject on the line var count = VisualTreeHelper.GetChildrenCount(parentElement); but it goes through if I have less items on the same list... – Beto Cupertino Mar 28 '12 at 15:01

1 Answers1

1

The list is virtual - controls are created as they are needed and potentially reused (I think).

Your options are to turn the ListBox to not be virtualized (override the template, and for the container, instead of a SerializedStackPanel, choose a regular StackPanel).

Your other (and preferable) option is to do the checking via Data Binding. Way easier and faster in most situations.

Shahar Prish
  • 4,838
  • 3
  • 26
  • 47
  • Thanks for your response, but how can I do the checking via Data Biding? You mean like adding items that are clicked to a new listbox? (I tried that, but what if I uncheck an item? How can I an update the listbox? Do you have any sample? I am still confused... – Beto Cupertino Mar 28 '12 at 15:32
  • You are binding {Items} to your ListBox - if each Item had a Boolean that signified whether or not it's checked, you could bind THAT to the Checkbox {IsChecked} dependency property and essentially just have it "work" – Shahar Prish Mar 28 '12 at 16:51
  • I was trying to understand and work around the problem I found this article: **ListBoxItems are destroyed if they are not within view.** Consequently ContainerFromItem() and ContainerFromIndex() return null since the ListBoxItems do not exist. This is apparently a memory/performance ( http://stackoverflow.com/questions/211971/scroll-wpf-listview-to-specific-line). That's exactly what is happening to my code, because if I test it with a few items it works perfeclty well... – Beto Cupertino Mar 29 '12 at 16:14
  • With that in mind I just force a way to view items on the screen using **Dispatcher.BeginInvoke(() => { ListCheckBoxVocabulary.SelectedIndex = ii;** I set a sleep thread (for testing) and it worked... I am generating a new ListBox keeping only the contents of checked checkbox (in the listbox)... I could not quite execute what you said, 'cause If I bind the checkboxes the new listbox generated will stay always the same, unckeck won't make a difference. I am going back to it, I must be missing something, thanks. – Beto Cupertino Mar 29 '12 at 17:23
  • Make the checkbox binding TwoWay and then when the user checks the box, your underlying data will change as well. – Shahar Prish Mar 29 '12 at 18:19