强制WPF在ItemsControl中创建项

强制WPF在ItemsControl中创建项,wpf,itemscontrol,Wpf,Itemscontrol,我想验证我的列表框中的项目是否在UI中正确显示。我想一种方法是遍历可视化树中列表框的所有子项,获取它们的文本,然后将其与我期望的文本进行比较 这种方法的问题是,在内部列表框使用虚拟化StackPanel来显示其项目,因此只创建可见的项目。我最终遇到了ItemContainerGenerator类,它似乎应该强制WPF在可视化树中为指定的项创建控件。不幸的是,这给我带来了一些奇怪的副作用。以下是生成列表框中所有项目的代码: List<string> generatedItems = n

我想验证我的
列表框中的项目是否在UI中正确显示。我想一种方法是遍历可视化树中
列表框
的所有子项,获取它们的文本,然后将其与我期望的文本进行比较

这种方法的问题是,在内部
列表框
使用
虚拟化StackPanel
来显示其项目,因此只创建可见的项目。我最终遇到了
ItemContainerGenerator
类,它似乎应该强制WPF在可视化树中为指定的项创建控件。不幸的是,这给我带来了一些奇怪的副作用。以下是生成
列表框中所有项目的代码:

List<string> generatedItems = new List<string>();
IItemContainerGenerator generator = this.ItemsListBox.ItemContainerGenerator;
GeneratorPosition pos = generator.GeneratorPositionFromIndex(-1);
using(generator.StartAt(pos, GeneratorDirection.Forward))
{
    bool isNewlyRealized;
    for(int i = 0; i < this.ItemsListBox.Items.Count; i++)
    {
        isNewlyRealized = false;
        DependencyObject cntr = generator.GenerateNext(out isNewlyRealized);
        if(isNewlyRealized)
        {
            generator.PrepareItemContainer(cntr);
        }

        string itemText = GetControlText(cntr);
        generatedItems.Add(itemText);
    }
}
List generatedItems=new List();
IItemContainerGenerator生成器=this.ItemsListBox.ItemContainerGenerator;
GeneratorPosition pos=发电机。GeneratorPositionFromIndex(-1);
使用(generator.StartAt(位置,GeneratorDirection.Forward))
{
布尔是新实现的;
for(int i=0;i
(如果您愿意,我可以提供
GetItemText()
的代码,但它只会遍历可视化树,直到找到
TextBlock
为止。我知道还有其他方法可以在项目中包含文本,但一旦项目生成工作正常,我会解决这个问题。)

在我的应用程序中,
ItemsListBox
包含20个项目,前12个项目最初可见。前14项的文本是正确的(可能是因为它们的控件已经生成)。但是,对于第15-20项,我根本没有收到任何文本。此外,如果我滚动到
项目列表框的底部
,则项目15-20的文本也为空。所以我似乎在干扰WPF生成控件的正常机制

我做错了什么?是否有其他/更好的方法强制将
ItemsControl
中的项添加到可视化树中

更新:我想我已经找到了发生这种情况的原因,尽管我不知道如何修复它。我的假设是,调用
PrepareItemContainer()
将生成任何必要的控件来显示项目,然后将容器添加到位于正确位置的可视化树中。事实证明,它并没有做这两件事。在我向下滚动查看之前,容器不会添加到
ItemsControl
,此时仅创建容器本身(即
ListBoxItem
),而不会创建其子项(此处应添加一些控件,其中一个应为显示项目文本的
TextBlock

如果遍历传递给
PrepareItemContainer()
的控件的可视树,结果是相同的。在这两种情况下,仅创建
ListBoxItem
,而不创建其子项


我找不到将
ListBoxItem
添加到可视化树的好方法。我在可视化树中找到了
virtualizangstackpanel
,但调用它的
子项。Add()
会导致
无效操作异常
(无法将项直接添加到
项面板
,因为它为其
项控件
生成项)。作为一个测试,我尝试使用反射调用它的
AddVisualChild()
(因为它是受保护的),但这也不起作用。

只是快速查看一下,如果列表框使用VirtualzingStackPanel-也许用类似StackPanel的StackPanel替换它就足够了

<ListBox.ItemsPanel>
  <ItemsPanelTemplate>
      <StackPanel/>
  <ItemsPanelTemplate>
<ListBox.ItemsPanel>

我想我知道怎么做了。问题是生成的项没有添加到可视化树中。经过一些搜索,我能想到的最好办法是调用
列表框中
虚拟化StackPanel
的一些受保护方法。虽然这并不理想,因为它只是为了测试,我想我不得不接受它

这就是我的工作原理:

VirtualizingStackPanel itemsPanel = null;
FrameworkElementFactory factory = control.ItemsPanel.VisualTree;
if(null != factory)
{
    // This method traverses the visual tree, searching for a control of
    // the specified type and name.
    itemsPanel = FindNamedDescendantOfType(control,
        factory.Type, null) as VirtualizingStackPanel;
}

List<string> generatedItems = new List<string>();
IItemContainerGenerator generator = this.ItemsListBox.ItemContainerGenerator;
GeneratorPosition pos = generator.GeneratorPositionFromIndex(-1);
using(generator.StartAt(pos, GeneratorDirection.Forward))
{
    bool isNewlyRealized;
    for(int i = 0; i < this.ItemsListBox.Items.Count; i++)
    {
        isNewlyRealized = false;
        UIElement cntr = generator.GenerateNext(out isNewlyRealized) as UIElement;
        if(isNewlyRealized)
        {
            if(i >= itemsPanel.Children.Count)
            {
                itemsPanel.GetType().InvokeMember("AddInternalChild",
                    BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMember,
                    Type.DefaultBinder, itemsPanel,
                    new object[] { cntr });
            }
            else
            {
                itemsPanel.GetType().InvokeMember("InsertInternalChild",
                    BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMember,
                    Type.DefaultBinder, itemsPanel,
                    new object[] { i, cntr });
            }

            generator.PrepareItemContainer(cntr);
        }

        string itemText = GetControlText(cntr);
        generatedItems.Add(itemText);
    }
}
virtualzingstackpanel itemsPanel=null;
FrameworkElement工厂=control.ItemsPanel.VisualTree;
如果(空!=工厂)
{
//此方法遍历可视树,搜索
//指定的类型和名称。
itemsPanel=FindNamedDescentOffType(控件,
factory.Type,null)作为虚拟化面板;
}
List generatedItems=新列表();
IItemContainerGenerator生成器=this.ItemsListBox.ItemContainerGenerator;
GeneratorPosition pos=发电机。GeneratorPositionFromIndex(-1);
使用(generator.StartAt(位置,GeneratorDirection.Forward))
{
布尔是新实现的;
for(int i=0;i=itemsPanel.Children.Count)
{
itemsPanel.GetType().InvokeMember(“AddInternalChild”,
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMember,
Type.DefaultBinder,itemsPanel,
新对象[]{cntr});
}
其他的
{
itemsPanel.GetType().InvokeMember(“InsertInternalChild”,
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMember,
Type.DefaultBinder,itemsPanel,
新对象[]{i,cntr});
}
发生器。准备电子容器(cntr);
}
string itemText=GetControlT
<DataTemplate DataType="{x:Type local:ProjectPersona}">
  <Grid Loaded="Row_Loaded">
    <!-- ... -->
  </Grid>
</DataTemplate>
private void Row_Loaded(object sender, RoutedEventArgs e)
{
    Grid grid = (Grid)sender;
    Carousel c = (Carousel)grid.FindName("carousel");
    ProjectPersona project = (ProjectPersona)grid.DataContext;
    if (project.SelectedTime != null)
        c.ScrollItemIntoView(project.SelectedTime);
}
    private FrameworkElement GetContainerForIndex(int index)
    {
        if (ItemsControl == null)
        {
            return null;
        }

        var container = ItemsControl.ItemContainerGenerator.ContainerFromIndex(index -1);
        if (container != null && container != DependencyProperty.UnsetValue)
        {
            return container as FrameworkElement;
        }
        else
        {

            var virtualizingPanel = FindVisualChild<VirtualizingPanel>(ItemsControl);
            if (virtualizingPanel == null)
            {
                // do something to load the (perhaps currently unloaded panel) once
            }
            virtualizingPanel = FindVisualChild<VirtualizingPanel>(ItemsControl);

            IItemContainerGenerator generator = ItemsControl.ItemContainerGenerator;
            using (generator.StartAt(generator.GeneratorPositionFromIndex(index), GeneratorDirection.Forward))
            {
                bool isNewlyRealized = false;
                container = generator.GenerateNext(out isNewlyRealized);
                if (isNewlyRealized)
                {
                    generator.PrepareItemContainer(container);
                    bool insert = false;
                    int pos = 0;
                    for (pos = virtualizingPanel.Children.Count - 1; pos >= 0; pos--)
                    {
                        var idx = ItemsControl.ItemContainerGenerator.IndexFromContainer(virtualizingPanel.Children[pos]);
                        if (!insert && idx < index)
                        {
                            ////Add
                            virtualizingPanel.GetType().InvokeMember("AddInternalChild", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, Type.DefaultBinder, virtualizingPanel, new object[] { container });
                            break;
                        }
                        else
                        {
                            insert = true;
                            if (insert && idx < index)
                            {
                                break;
                            }
                        }
                    }

                    if (insert)
                    {
                        virtualizingPanel.GetType().InvokeMember("InsertInternalChild", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, Type.DefaultBinder, virtualizingPanel, new object[] { pos + 1, container });
                    }
                }

                return container as FrameworkElement;
            }
        }
    }
    public static bool FocusSelectedItem(this ListBox listbox)
    {
        int ix;
        if ((ix = listbox.SelectedIndex) < 0)
            return false;

        var icg = listbox.ItemContainerGenerator;
        if (icg.Status == GeneratorStatus.NotStarted)
            listbox.UpdateLayout();

        var el = (UIElement)icg.ContainerFromIndex(ix);
        if (el == null)
            return false;

        listbox.ScrollIntoView(el);

        return el == Keyboard.Focus(el);
    }