强制WPF在ItemsControl中创建项目 [英] Forcing WPF to create the items in an ItemsControl

查看:62
本文介绍了强制WPF在ItemsControl中创建项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想验证ListBox中的项目是否在UI中正确显示.我想出一种方法是遍历视觉树中ListBox的所有子级,获取其文本,然后将其与我期望的文本进行比较.

I want to verify that the items in my ListBox are displayed correctly in the UI. I figured one way to do this is to go through all of the children of the ListBox in the visual tree, get their text, and then compare that with what I expect the text to be.

此方法的问题在于,内部ListBox在内部使用VirtualizingStackPanel来显示其项目,因此仅创建可见的项目.我最终遇到了ItemContainerGenerator类,看起来它应该强制WPF在可视树中为指定项目创建控件.不幸的是,这给我造成了一些怪异的影响.这是我的代码,用于生成ListBox中的所有项目:

The problem with this approach is that internally ListBox uses a VirtualizingStackPanel to display its items, so only the items that are visible are created. I eventually came across the ItemContainerGenerator class, which looks like it should force WPF to create the controls in the visual tree for the specified item. Unfortunately, that is causing some weird side affects for me. Here is my code to generate all of the items in the ListBox:

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

(如果愿意,我可以提供GetItemText()的代码,但是它会遍历可视化树直到找到TextBlock.我意识到这是在项目中添加文本的其他方式,但是我一旦我使项目生成正常运行,就将其修复.)

(I can provide the code for GetItemText() if you'd like, but it just traverses the visual tree until a TextBlock is found. I realize that ther are other ways to have text in an item, but I'll fix that up once I get item generation working properly.)

在我的应用中,ItemsListBox包含20个项目,最初显示的前12个项目.前14个项目的文本正确(可能是因为它们的控件已经生成).但是,对于项目15-20,我什么都没收到.另外,如果我滚动到ItemsListBox的底部,则项目15-20的文本也为空白.因此,似乎我在干扰WPF的常规机制以某种方式生成控件.

In my app, ItemsListBox contains 20 items, with the first 12 items initially visible. The text for the first 14 items is correct (likely because their controls have already been generated). However, for items 15-20, I don't get any text at all. In addition, if I scroll to the bottom of the ItemsListBox, the text of items 15-20 is also blank. So it seems like I'm interfering with WPF's normal mechanism for generating controls some how.

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

What am I doing wrong? Is there a different/better way of forcing the items in an ItemsControl to be added to the visual tree?

更新:尽管我不知道如何解决,但我认为我已经找到了发生这种情况的原因.我假设对PrepareItemContainer()的调用将生成任何必要的控件来显示该项目,然后将容器添加到正确位置的可视树中.事实证明,它没有做任何一件事情.直到我向下滚动查看该容器时,才会将其添加到ItemsControl中,那时仅创建了容器本身(即ListBoxItem)-未创建其子级(此处应添加一些控件) ,其中之一应该是显示项目文本的TextBlock.

Update: I think that I have found why this is occurring, although I do not know how to fix it. My assumption that the call to PrepareItemContainer() would generate any necessary controls to display the item, and then add the container to the visual tree in the correct location. It turns out that it is not doing either of these things. The container isn't added to the ItemsControl until I scroll down to view it, and at that time only the container itself (i.e. ListBoxItem) is created - its children are not created (there should be a few controls added here, one of which should be the TextBlock that will display the text of the item).

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

If I traverse the visual tree of the control that I passed to PrepareItemContainer() the results are the same. In both cases only the ListBoxItem is created, and none of its children are created.

我找不到将ListBoxItem添加到可视树的好方法.我在视觉树中找到了VirtualizingStackPanel,但是调用它的Children.Add()会得到一个InvalidOperationException(无法将项目直接添加到ItemPanel中,因为它会为其ItemsControl生成项目).就像一个测试一样,我尝试使用反射来调用它的AddVisualChild()(因为它是受保护的),但这也不起作用.

I could not find a good way to add the ListBoxItem to the visual tree. I found the VirtualizingStackPanel in the visual tree, but calling its Children.Add() results in an InvalidOperationException (cannot add items directly to the ItemPanel, since it generates items for its ItemsControl). Just as a test, I tried calling its AddVisualChild() using Reflection (since it is protected), but that didn't work, either.

推荐答案

我想我已经知道如何做到这一点.问题在于生成的项目没有添加到可视化树中.经过一些搜索之后,我能想到的最好的办法是在ListBox中调用VirtualizingStackPanel的某些受保护方法.虽然这并不理想,但由于它仅用于测试,我认为我将不得不忍受它.

I think I figured out how to do this. The problem was that the generated items were not added to the visual tree. After some searching, the best I could come up with is to call some protected methods of the VirtualizingStackPanel in the ListBox. While this isn't ideal, since it's only for testing I think I'm going to have to live with it.

这对我有用:

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

这篇关于强制WPF在ItemsControl中创建项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆