ItemContainerGenerator.ContainerFromItem() returns null while VirtualizingStackPanel.IsVirtualizing="False"
Asked Answered
E

4

5

I'm facing a similar problem with this question however VirtualizingStackPanel.IsVirtualizing="False" didn't solve my problem. Is there anyone facing the same issue?

The thing is I have a custom combobox,

<Style TargetType="{x:Type MultiSelectionComboBox}"  >
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Vertical"
                VerticalAlignment="Center"
                HorizontalAlignment="Center"
                VirtualizingStackPanel.IsVirtualizing="False"/>
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="ItemTemplate">
        <Setter.Value>
            <DataTemplate>
                <StackPanel Orientation="Horizontal" x:Name="ItemStack" VirtualizingStackPanel.IsVirtualizing="False">
                    <CheckBox x:Name="CheckBoxItem"
                        Command="{Binding SelectItem, RelativeSource={RelativeSource AncestorType={x:Type MultiSelectionComboBox}}}"
                        CommandParameter="{Binding Key}"
                              >
                    </CheckBox>
                    <TextBlock Text="{Binding DisplayText}"></TextBlock>
                </StackPanel>
            </DataTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ComboBox}">
                <Grid x:Name="Placement" SnapsToDevicePixels="true">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Border BorderThickness="1" BorderBrush="Black">
                        <TextBox IsReadOnly="True" Grid.Column="0" 
                                 Text="{Binding Text, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType={x:Type MultiSelectionComboBox}}}">
                        </TextBox>
                    </Border>
                    <Popup x:Name="PART_Popup" 
                               Grid.Column="0"
                               Focusable="False"
                               Grid.ColumnSpan="2"
                               IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
                               Placement="Bottom"
                               VerticalOffset="-1"
                               PlacementTarget="{Binding ElementName=LayoutRoot}">
                        <Popup.Resources>
                            <Style TargetType="{x:Type ScrollBar}" BasedOn="{StaticResource {x:Type ScrollBar}}">
                                <Style.Triggers>
                                    <Trigger Property="Orientation" Value="Vertical">
                                        <Setter Property="BorderThickness" Value="0"/>
                                    </Trigger>
                                </Style.Triggers>
                            </Style>
                        </Popup.Resources>
                        <ScrollViewer x:Name="DropDownScrollViewer"
                                          Background="{StaticResource Background}"
                                          BorderBrush="{TemplateBinding BorderBrush}"
                                          BorderThickness="{TemplateBinding BorderThickness}"
                                          MinWidth="{Binding ActualWidth, ElementName=LayoutRoot}"
                                          MaxHeight="{TemplateBinding MaxDropDownHeight}">
                            <ItemsPresenter KeyboardNavigation.DirectionalNavigation="Contained"/>
                        </ScrollViewer>
                    </Popup>
                    <ToggleButton IsEnabled="{Binding IsEnabled, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType={x:Type MultiSelectionComboBox}}}" Grid.Column="1" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ComboBoxToggleButton}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
</ResourceDictionary>

and yet I can't get a reference to the checkbox inside via,

this.ItemContainerGenerator.ContainerFromItem(this.Items[0]) as ComboBoxItem;

Is there any suggestions?

What i actually want to achieve is,

i want to change checkboxes ischecked property which is depending on an other object which can change on runtime. I can't do it with using bindings due to the current state of the overall project which i can not change at this point. So basically once the new MultiSelectionComboBox is created i want to do something like this,

foreach (object item in this.Items)
{
    ComboBoxItem comboBoxItem = this.ItemContainerGenerator.ContainerFromItem(item) as ComboBoxItem;
    if (comboBoxItem == null)
        continue;
    FrameworkElement element = comboBoxItem.ContentTemplate.LoadContent() as FrameworkElement;
    CheckBox checkBox = element.FindName("CheckBoxItem") as CheckBox;
    checkBox.IsChecked = this.SelectedItem.Contains(item);
}
Ewall answered 24/4, 2013 at 5:39 Comment(6)
What does your custom ComboBox look like? Please post the full XAML of the ComboBox.Aryan
the suggestion you gave before setting VirtualizingStackPanel.IsVirtualizing="False" to ItemsPanelTemplate resulted in some improvement. however it does still return null where i need it not to.Ewall
Are you sure that MultiSelectionComboBox actually uses ComboBoxItem as container type? And by the way, setting VirtualizingStackPanel.IsVirtualizing on a StackPanel also has no effect. I guess virtualization is not the problem here.Aryan
if i'm correct, it seems like this.ItemContainerGenerator.ContainerFromItem(item) returns null when the dropdown is not opened thus not rendered. After setting IsVirtualizing false to ItemsPanelTamplete. Is there a way to get the item before the dropdown is renderedEwall
When does your ItemContainerGenerator.ContainerFromItem get run? You may need to be sure that the containers have been generated first. Also, if you don't cast it as a ComboBoxItem, does it return a value? ContainerFromItem might just return your template in a ContentPresenter, which gets later wrapped in a ComboBoxItem, so its possible the cast is simply invalid which is why you're getting nullCogitative
the cast is valid, im checking it while in debug. I'm not quite sure if the containers are generated. I did solve my problem using an other approach link. But out of curiosity how can i be sure if the containers are actually generated. Thanks for everything.Ewall
T
12

try execute UpdateLayout() before this.ItemContainerGenerator.ContainerFromItem(item)

Titus answered 21/2, 2014 at 12:27 Comment(1)
Brilliant! I lost a whole hour on the problem trying different suggestions and this answer helped me!Qua
E
2

Use ItemContainerGenerator.StatusChanged event from you ComboBox like this:

myComboBox.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;

void ItemContainerGenerator_StatusChanged(object sender, System.EventArgs e)
{
    if (myComboBox.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
    {
        foreach (var item in myComboBox.Items)
        {
            var container = (ComboBoxItem)LanguageComboBox.ItemContainerGenerator.ContainerFromItem(item);
        }
    }
}
Euell answered 3/4, 2014 at 9:38 Comment(0)
P
0

As my logic was in the SelectionChanged event, i wondered why the ItemContainerGenerator.ContainerFromItem method always returned null even if the Listbox.SelectedItem was not null and even more strange, Virtualisation was turned off! Looking at the ItemContainerGenerator.Status i saw that it was Primitives.GeneratorStatus.NotStarted then i added a simple test on ItemContainerGenerator.Status == Primitives.GeneratorStatus.ContainersGenerated and finally solved it that way and no need to subsribe to the Status_Changed event.

Pleochroism answered 8/8, 2016 at 15:52 Comment(0)
S
0

The UpdateLayout (commented by R.Titov) solved for me. And I do not need to change the virtualization configuration

Somite answered 31/8, 2023 at 14:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.