Elemento contiene generador de energía.ContainerFromItem () devuelve null?


Estoy teniendo un poco de comportamiento extraño que parece que no puedo resolver. Cuando itere a través de los elementos en mi ListBox.ItemsSource propiedad, parece que no puedo conseguir el contenedor? Espero ver un ListBoxItem devuelto, pero solo obtengo null.

¿Alguna idea?

Aquí está el bit de código que estoy usando:

this.lstResults.ItemsSource.ForEach(t =>
    {
        ListBoxItem lbi = this.lstResults.ItemContainerGenerator.ContainerFromItem(t) as ListBoxItem;

        if (lbi != null)
        {
            this.AddToolTip(lbi);
        }
    });

El ItemsSource está actualmente establecido en un diccionario y contiene un número de KVPs.

Author: H.B., 2011-07-16

10 answers

Encontré algo que funcionó mejor para mi caso en esta pregunta de StackOverflow:

Obtener fila en datagrid

Al poner UpdateLayout y un scrollIntoView llama antes de llamar a ContainerFromItem o ContainerFromIndex, se hace que se realice esa parte de la cuadrícula de datos que hace posible que devuelva un valor para ContainerFromItem/ContainerFromIndex:

dataGrid.UpdateLayout();
dataGrid.ScrollIntoView(dataGrid.Items[index]);
var row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(index);

Si no desea que cambie la ubicación actual en la cuadrícula de datos, probablemente no sea una buena solución para usted, pero si eso está bien, funciona sin tener que desactivar la virtualización.

 43
Author: Phred Menyhert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:53:50

Finalmente solucionó el problema... Al agregar VirtualizingStackPanel.IsVirtualizing="False" a mi XAML, ahora todo funciona como se esperaba.

En el lado negativo, me pierdo todos los beneficios de rendimiento de la virtualización, así que cambié mi enrutamiento de carga a async y agregué un "spinner" en mi listbox mientras se carga...

 14
Author: Sonny Boy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-18 16:43:00

Paso a través del código con el depurador y ver si realmente no hay nada retraído o si el as-cast es simplemente incorrecto y por lo tanto se convierte en null (usted podría utilizar un cast normal para obtener una excepción adecuada).

Un problema que ocurre con frecuencia es que cuando un ItemsControl está virtualizando para la mayoría de los elementos, ningún contenedor existirá en ningún momento.

Tampoco recomendaría tratar con los contenedores de elementos directamente, sino más bien vincular las propiedades y suscribirse a eventos (a través del ItemsControl.ItemContainerStyle).

 6
Author: H.B.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-20 14:39:15
object viewItem = list.ItemContainerGenerator.ContainerFromItem(item);
if (viewItem == null)
{
    list.UpdateLayout();
    viewItem = list.ItemContainerGenerator.ContainerFromItem(item);
    Debug.Assert(viewItem != null, "list.ItemContainerGenerator.ContainerFromItem(item) is null, even after UpdateLayout");
}
 6
Author: epox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-01 23:43:28

Utilice esta suscripción:

TheListBox.ItemContainerGenerator.StatusChanged += (sender, e) =>
{
  TheListBox.Dispatcher.Invoke(() =>
  {
     var TheOne = TheListBox.ItemContainerGenerator.ContainerFromIndex(0);
     if (TheOne != null)
       // Use The One
  });
};
 3
Author: Amir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-15 09:48:30

Llego un poco tarde a la fiesta, pero aquí hay otra solución que es a prueba de fallos en mi caso,

Después de probar muchas soluciones que sugieren agregar IsExpanded y IsSelected al objeto subyacente y vincularlos en el estilo TreeViewItem, mientras que este funciona principalmente en algunos casos todavía falla ...

Nota: mi objetivo era escribir una vista mini / personalizada como Explorador donde cuando hago clic en una carpeta en el panel derecho se selecciona en el TreeView, al igual que en Explorador.

private void ListViewItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    var item = sender as ListViewItem;
    var node = item?.Content as DirectoryNode;
    if (node == null) return;

    var nodes = (IEnumerable<DirectoryNode>)TreeView.ItemsSource;
    if (nodes == null) return;

    var queue = new Stack<Node>();
    queue.Push(node);
    var parent = node.Parent;
    while (parent != null)
    {
        queue.Push(parent);
        parent = parent.Parent;
    }

    var generator = TreeView.ItemContainerGenerator;
    while (queue.Count > 0)
    {
        var dequeue = queue.Pop();
        TreeView.UpdateLayout();
        var treeViewItem = (TreeViewItem)generator.ContainerFromItem(dequeue);
        if (queue.Count > 0) treeViewItem.IsExpanded = true;
        else treeViewItem.IsSelected = true;
        generator = treeViewItem.ItemContainerGenerator;
    }
}

Múltiples trucos utilizados aquí:

  • una pila para expandir cada elemento de arriba a abajo
  • asegúrese de utilizar el nivel actual generador para encontrar el elemento (realmente importante)
  • el hecho de que el generador para elementos de nivel superior nunca devuelva null

Hasta ahora funciona muy bien,

  • no hay necesidad de contaminar sus tipos con nuevas propiedades
  • no es necesario deshabilitar la virtualización en all .
 3
Author: Aybe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-05 20:39:13

Aunque deshabilitar la virtualización desde XAML funciona, creo que es mejor deshabilitarlo desde el .archivo cs que utiliza ContainerFromItem

 VirtualizingStackPanel.SetIsVirtualizing(listBox, false);

De esa manera, se reduce el acoplamiento entre el XAML y el código; así se evita el riesgo de que alguien rompa el código tocando el XAML.

 2
Author: Benoit Blanchon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-02 13:09:18

VirtualizingStackPanel.IsVirtualizing = "False" Hace que el control sea borroso . Véase la aplicación que figura a continuación. Lo que me ayuda a evitar el mismo problema. Configure su aplicación VirtualizingStackPanel.IsVirtualizing = "True" siempre.

Consulte el enlace para obtener información detallada

/// <summary>
/// Recursively search for an item in this subtree.
/// </summary>
/// <param name="container">
/// The parent ItemsControl. This can be a TreeView or a TreeViewItem.
/// </param>
/// <param name="item">
/// The item to search for.
/// </param>
/// <returns>
/// The TreeViewItem that contains the specified item.
/// </returns>
private TreeViewItem GetTreeViewItem(ItemsControl container, object item)
{
    if (container != null)
    {
        if (container.DataContext == item)
        {
            return container as TreeViewItem;
        }

        // Expand the current container
        if (container is TreeViewItem && !((TreeViewItem)container).IsExpanded)
        {
            container.SetValue(TreeViewItem.IsExpandedProperty, true);
        }

        // Try to generate the ItemsPresenter and the ItemsPanel.
        // by calling ApplyTemplate.  Note that in the 
        // virtualizing case even if the item is marked 
        // expanded we still need to do this step in order to 
        // regenerate the visuals because they may have been virtualized away.

        container.ApplyTemplate();
        ItemsPresenter itemsPresenter = 
            (ItemsPresenter)container.Template.FindName("ItemsHost", container);
        if (itemsPresenter != null)
        {
            itemsPresenter.ApplyTemplate();
        }
        else
        {
            // The Tree template has not named the ItemsPresenter, 
            // so walk the descendents and find the child.
            itemsPresenter = FindVisualChild<ItemsPresenter>(container);
            if (itemsPresenter == null)
            {
                container.UpdateLayout();

                itemsPresenter = FindVisualChild<ItemsPresenter>(container);
            }
        }

        Panel itemsHostPanel = (Panel)VisualTreeHelper.GetChild(itemsPresenter, 0);


        // Ensure that the generator for this panel has been created.
        UIElementCollection children = itemsHostPanel.Children; 

        MyVirtualizingStackPanel virtualizingPanel = 
            itemsHostPanel as MyVirtualizingStackPanel;

        for (int i = 0, count = container.Items.Count; i < count; i++)
        {
            TreeViewItem subContainer;
            if (virtualizingPanel != null)
            {
                // Bring the item into view so 
                // that the container will be generated.
                virtualizingPanel.BringIntoView(i);

                subContainer = 
                    (TreeViewItem)container.ItemContainerGenerator.
                    ContainerFromIndex(i);
            }
            else
            {
                subContainer = 
                    (TreeViewItem)container.ItemContainerGenerator.
                    ContainerFromIndex(i);

                // Bring the item into view to maintain the 
                // same behavior as with a virtualizing panel.
                subContainer.BringIntoView();
            }

            if (subContainer != null)
            {
                // Search the next level for the object.
                TreeViewItem resultContainer = GetTreeViewItem(subContainer, item);
                if (resultContainer != null)
                {
                    return resultContainer;
                }
                else
                {
                    // The object is not under this TreeViewItem
                    // so collapse it.
                    subContainer.IsExpanded = false;
                }
            }
        }
    }

    return null;
}
 1
Author: PropertyChangedEventHandler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-23 08:34:45

Para cualquiera que todavía tenga problemas con esto, pude solucionar este problema ignorando el primer evento de cambio de selección y utilizando un hilo para repetir básicamente la llamada. Esto es lo que terminé haciendo:

private int _hackyfix = 0;
    private void OnMediaSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        //HACKYFIX:Hacky workaround for an api issue
        //Microsoft's api for getting item controls for the flipview item fail on the very first media selection change for some reason.  Basically we ignore the
        //first media selection changed event but spawn a thread to redo the ignored selection changed, hopefully allowing time for whatever is going on
        //with the api to get things sorted out so we can call the "ContainerFromItem" function and actually get the control we need I ignore the event twice just in case but I think you can get away with ignoring only the first one.
        if (_hackyfix == 0 || _hackyfix == 1)
        {
            _hackyfix++;
            Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            OnMediaSelectionChanged(sender, e);
        });
        }
        //END OF HACKY FIX//Actual code you need to run goes here}

EDITAR 29/10/2014: En realidad ni siquiera necesitas el código del despachador de subprocesos. Puede establecer lo que necesite en null para activar el primer evento de cambio de selección y luego volver fuera del evento para que los eventos futuros funcionen como se esperaba.

        private int _hackyfix = 0;
    private void OnMediaSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        //HACKYFIX: Daniel note:  Very hacky workaround for an api issue
        //Microsoft's api for getting item controls for the flipview item fail on the very first media selection change for some reason.  Basically we ignore the
        //first media selection changed event but spawn a thread to redo the ignored selection changed, hopefully allowing time for whatever is going on
        //with the api to get things sorted out so we can call the "ContainerFromItem" function and actually get the control we need
        if (_hackyfix == 0)
        {
            _hackyfix++;
            /*
            Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            OnMediaSelectionChanged(sender, e);
        });*/
            return;
        }
        //END OF HACKY FIX
        //Your selection_changed code here
        }
 1
Author: Daniel Caban,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-29 12:41:27

Lo más probable es que este sea un problema relacionado con la virtualización, por lo que ListBoxItem los contenedores se generan solo para los elementos visibles actualmente (consulte https://msdn.microsoft.com/en-us/library/system.windows.controls.virtualizingstackpanel(v=vs. 110).aspx#Anchor_9)

Si está utilizando ListBox Le sugeriría cambiar a ListView en su lugar: hereda de ListBox y admite el método ScrollIntoView() que puede utilizar para controlar la virtualización;

targetListView.ScrollIntoView(itemVM);
DoEvents();
ListViewItem itemContainer = targetListView.ItemContainerGenerator.ContainerFromItem(itemVM) as ListViewItem;

(el ejemplo anterior también utiliza el DoEvents() método estático explicado con más detalle aquí; WPF ¿cómo esperar a que se produzca la actualización de enlace antes de procesar más código?)

Hay algunas otras diferencias menores entre los controles ListBox y ListView (Cuál es la diferencia entre ListBox y ListView) - que esencialmente no debería afectar su caso de uso.

 0
Author: fvojvodic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:25:22