Wie funktioniert ItemContainerGenerator.ContainerFromItem mit einer gruppierten Liste arbeiten?

StackOverflow https://stackoverflow.com/questions/165424

  •  03-07-2019
  •  | 
  •  

Frage

Ich habe eine ListBox, die bis vor kurzem eine flache Liste der Elemente anzeigte. Ich konnte myList.ItemContainerGenerator.ConainerFromItem (Ding) verwenden, um die ListBoxItem Hosting „Ding“ in der Liste abgerufen werden.

Diese Woche habe ich die List-Box geändert haben etwas, dass der Collection, dass es für seine Elemente bindet hat Gruppierung aktiviert. Nun werden die Elemente in der List-Box unterhalb schöne Header gruppiert.

Dies ist jedoch seit tun hat ItemContainerGenerator.ContainerFromItem funktioniert nicht mehr - es gibt null zurück, auch für Gegenstände, die ich kenne, sind in der List-Box. Heck - ContainerFromIndex (0) zurückkehrt null, selbst wenn die List-Box mit vielen Elementen bestückt ist

!

Wie rufe ich einen ListBoxItem aus einem ListBox, die gruppierte Artikel ist angezeigt wird?

Edit: Hier ist der XAML und Code-Behind für ein abgespeckte Beispiel. Dies wirft eine Nullreferenceexception, weil ContainerFromIndex (1) zurückkehrt null, obwohl es vier Elemente in der Liste.

XAML:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
    Title="Window1">

    <Window.Resources>
        <XmlDataProvider x:Key="myTasks" XPath="Tasks/Task">
            <x:XData>
                <Tasks xmlns="">
                    <Task Name="Groceries" Type="Home"/>
                    <Task Name="Cleaning" Type="Home"/>
                    <Task Name="Coding" Type="Work"/>
                    <Task Name="Meetings" Type="Work"/>
                </Tasks>
            </x:XData>
        </XmlDataProvider>

        <CollectionViewSource x:Key="mySortedTasks" Source="{StaticResource myTasks}">
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="@Type" />
                <scm:SortDescription PropertyName="@Name" />
            </CollectionViewSource.SortDescriptions>

            <CollectionViewSource.GroupDescriptions>
                <PropertyGroupDescription PropertyName="@Type" />
            </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
    </Window.Resources>

    <ListBox 
        x:Name="listBox1" 
        ItemsSource="{Binding Source={StaticResource mySortedTasks}}" 
        DisplayMemberPath="@Name"
        >
        <ListBox.GroupStyle>
            <GroupStyle>
                <GroupStyle.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Name}"/>
                    </DataTemplate>
                </GroupStyle.HeaderTemplate>
            </GroupStyle>
        </ListBox.GroupStyle>
    </ListBox>
</Window>

CS:

public Window1()
{
    InitializeComponent();
    listBox1.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
}

void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
    if (listBox1.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
    {
        listBox1.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged;

        var i = listBox1.ItemContainerGenerator.ContainerFromIndex(1) as ListBoxItem;

        // select and keyboard-focus the second item
        i.IsSelected = true;
        i.Focus();
    }
}
War es hilfreich?

Lösung

Sie Haben hören und auf das ItemsGenerator.StatusChanged Ereignis zu reagieren und warten, bis die ItemContainers erzeugt werden, bevor Sie sie mit ContainerFromElement zugreifen können.


Suche weiter, ich habe festgestellt, ein Thread in der MSDN-Forum von jemandem, der das gleiche Problem hat. Dies scheint ein Bug in WPF zu sein, wenn man einen Satz Group hat. Die Lösung ist, den Zugang des ItemGenerator nach dem Rendering-Prozess nach Punt. Unten ist der Code für Ihre Frage. Ich versuchte dies und es funktioniert:

    void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
    {
        if (listBox1.ItemContainerGenerator.Status
            == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
        {
            listBox1.ItemContainerGenerator.StatusChanged
                -= ItemContainerGenerator_StatusChanged;
            Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input,
                new Action(DelayedAction));
        }
    }

    void DelayedAction()
    {
        var i = listBox1.ItemContainerGenerator.ContainerFromIndex(1) as ListBoxItem;

        // select and keyboard-focus the second item
        i.IsSelected = true;
        i.Focus();
    }

Andere Tipps

Wenn der obige Code nicht für Sie arbeiten, geben diesem einen Versuch

public class ListBoxExtenders : DependencyObject
{
    public static readonly DependencyProperty AutoScrollToCurrentItemProperty = DependencyProperty.RegisterAttached("AutoScrollToCurrentItem", typeof(bool), typeof(ListBoxExtenders), new UIPropertyMetadata(default(bool), OnAutoScrollToCurrentItemChanged));

    public static bool GetAutoScrollToCurrentItem(DependencyObject obj)
    {
        return (bool)obj.GetValue(AutoScrollToSelectedItemProperty);
    }

    public static void SetAutoScrollToCurrentItem(DependencyObject obj, bool value)
    {
        obj.SetValue(AutoScrollToSelectedItemProperty, value);
    }

    public static void OnAutoScrollToCurrentItemChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
    {
        var listBox = s as ListBox;
        if (listBox != null)
        {
            var listBoxItems = listBox.Items;
            if (listBoxItems != null)
            {
                var newValue = (bool)e.NewValue;

                var autoScrollToCurrentItemWorker = new EventHandler((s1, e2) => OnAutoScrollToCurrentItem(listBox, listBox.Items.CurrentPosition));

                if (newValue)
                    listBoxItems.CurrentChanged += autoScrollToCurrentItemWorker;
                else
                    listBoxItems.CurrentChanged -= autoScrollToCurrentItemWorker;
            }
        }
    }

    public static void OnAutoScrollToCurrentItem(ListBox listBox, int index)
    {
        if (listBox != null && listBox.Items != null && listBox.Items.Count > index && index >= 0)
            listBox.ScrollIntoView(listBox.Items[index]);
    }

}

Verwendung in XAML

<ListBox IsSynchronizedWithCurrentItem="True" extenders:ListBoxExtenders.AutoScrollToCurrentItem="True" ..../>

Versuchen Sie, die VisualTree up aus dem ‚Ding‘ Parsen, bis Sie einen ListBoxItem Typ

erreichen
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top