代码之家  ›  专栏  ›  技术社区  ›  Matt Hamilton

ItemContainerGenerator.ContainerFromItem如何处理分组列表?

  •  27
  • Matt Hamilton  · 技术社区  · 16 年前

    我有一个列表框,直到最近才显示一个简单的项目列表。我能够使用myList.ItemContainerGenerator.ConainerFromItem(thing)检索列表中承载“thing”的ListBoxItem。

    但是,由于执行此操作,ItemContainerGenerator.ContainerFromItem已停止工作-即使对于我知道在列表框中的项目,它也会返回null。Heck-ContainerFromIndex(0)返回null,即使列表框中填充了许多项!

    如何从显示分组项的ListBox中检索ListBoxItem?

    编辑:下面是一个精简示例的XAML和代码。这会引发NullReferenceException,因为即使列表中有四项,ContainerFromIndex(1)仍返回null。

    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>
    

    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();
        }
    }
    
    3 回复  |  直到 16 年前
        1
  •  40
  •   Tatiana Racheva    12 年前

    ItemsGenerator.StatusChanged 事件并等待生成ItemContainers,然后才能使用ContainerRomeElement访问它们。


    进一步搜索,我发现 a thread in the MSDN forum 来自有同样问题的人。这似乎是WPF中的一个bug,当一个人有一个GroupStyle集时。解决方案是在渲染过程之后取消ItemGenerator的访问权。下面是您的问题代码。我试过这个,效果很好:

        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();
        }
    
        2
  •  2
  •   D.Kempkes    10 年前

    如果上面的代码不适合您,请尝试一下

    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]);
        }
    
    }
    

    XAML中的用法

    <ListBox IsSynchronizedWithCurrentItem="True" extenders:ListBoxExtenders.AutoScrollToCurrentItem="True" ..../>
    
        3
  •  0
  •   Jobi Joy    16 年前

    尝试从“thing”向上解析VisualTree,直到达到ListBoxItem类型