WPF无法使ItemContainerGenerator.ContainerFromItem工作

时间:2013-12-31 22:06:56

标签: wpf treeview wpf-controls

我看过herehere以及其他许多地方,但我似乎无法使用ItemContainerGenerator.ContainerFromItem方法来处理WPF TreeView!我试图传递我想看的实际项目,但没有得到任何地方,我只是试图获取我的TreeView中的第一项。这是我的示例代码:

private static bool ExpandAndSelectItem(ItemsControl parentContainer, object itemToSelect)
{
    // This doesn't work.
    parentContainer.BringIntoView();
    // May be virtualized, bring into view and try again.
    parentContainer.UpdateLayout();
    parentContainer.ApplyTemplate();

    TreeViewItem topItem = (TreeViewItem)parentContainer.ItemContainerGenerator.ContainerFromItem(parentContainer.Items[0]);

    // Can't find child container unless the parent node is Expanded once
    if ((topItem != null) && !topItem.IsExpanded) 
    {
        topItem.IsExpanded = true;
        parentContainer.UpdateLayout();
    }
}

正如您所看到的,我尝试调用许多“更新”方法来尝试使TreeView“可见”和“可访问”。 Catch-22似乎是你不能使用ContainerFromItem(),除非第一个TreeViewItem被扩展,但我不能抓住TreeViewItem来扩展它,直到ContainerFromItem()工作!

另一个有趣的事情是:当我打开这个窗口(它是一个UserControl)时,ContainerFromItem()返回空值,但是如果我关闭窗口并将其打开,ContainerFromItem()启动返回非空值。有什么事情我应该寻找或强行解雇?

3 个答案:

答案 0 :(得分:4)

原来我正在寻找的事件是“已加载”。我只是在XAML中将一个事件处理程序附加到我的树视图中,并在该事件处理程序中调用了我的逻辑。

<TreeView x:Name="MyTreeView"
          Margin="0,5,0,5"
          HorizontalAlignment="Left"
          BorderThickness="0"
          FontSize="18"
          FontFamily="Segoe WP"
          MaxWidth="900"
          Focusable="True"
          Loaded="MyTreeView_Load">
    ...
</TreeView>

事件处理程序:

private void MyTreeView_Load(object sender, RoutedEventArgs e)
{
    ShowSelectedThing(MyTreeView, ThingToFind);
}
// Gotta call the TreeView an ItemsControl to cast it between TreeView and TreeViewItem
// as you recurse
private static bool ShowSelectedThing(ItemsControl parentContainer, object ThingToFind)
{
    // check current level of tree
    foreach (object item in parentContainer.Items)
    {
        TreeViewItem currentContainer = (TreeViewItem)parentContainer.ItemContainerGenerator.ContainerFromItem(item);
        if ((currentContainer != null) && (item == ThingToFind)
        {
            currentContainer.IsSelected = true;
            currentContainer.BringIntoView();
            return true;
        }
    }
    // item is not found at current level, check the kids
    foreach (object item in parentContainer.Items)
    {
        TreeViewItem currentContainer = (TreeViewItem)parentContainer.ItemContainerGenerator.ContainerFromItem(item);
        if ((currentContainer != null) && (currentContainer.Items.Count > 0))
        {
            // Have to expand the currentContainer or you can't look at the children
            currentContainer.IsExpanded = true;
            currentContainer.UpdateLayout();
            if (!ShowSelectedThing(currentContainer, ThingToFind))
            {
                // Haven't found the thing, so collapse it back
                currentContainer.IsExpanded = false;
            }
            else
            {
                // We found the thing
                return true;
            }
        }
    }
    // default
    return false;
}

希望这有助于某人。有时在现实世界中,对于要求苛刻的客户,奇怪的要求和短期限,你必须破解!

答案 1 :(得分:0)

当容器生成器的状态为“未启动”时或者&#39; ContainersGenerating&#39;,您无法找到容器。

使用此方法查找数据项的容器。

    private static async Task<TreeViewItem> FindItemContainer(ItemsControl itemsControl, object item)
    {
        var generator = itemsControl.ItemContainerGenerator;
        if (generator.Status != GeneratorStatus.ContainersGenerated)
        {
            var tcs = new TaskCompletionSource<object>();
            EventHandler handler = null;
            handler = (s, e) =>
            {
                if (generator.Status == GeneratorStatus.ContainersGenerated)
                {
                    generator.StatusChanged -= handler;
                    tcs.SetResult(null);
                }
                else if (generator.Status == GeneratorStatus.Error)
                {
                    generator.StatusChanged -= handler;
                    tcs.SetException(new InvalidOperationException());
                }
            };
            generator.StatusChanged += handler;
            if (itemsControl is TreeViewItem tvi)
                tvi.IsExpanded = true;
            itemsControl.UpdateLayout();
            await tcs.Task;
        }

        var container = (TreeViewItem)generator.ContainerFromItem(item);
        if(container == null)
        {
            foreach (var parentItem in itemsControl.Items)
            {
                var parentContainer = (TreeViewItem)generator.ContainerFromItem(parentItem);
                container = await FindItemContainer(parentContainer, item);
                if (container != null)
                    return container;
            }
        }
        return container;
    }

答案 2 :(得分:0)

    private void Lv_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        ListView Lv = (ListView)sender;
        Lv.UpdateLayout();           // 1.step
        DependencyObject Dep = Lv.ItemContainerGenerator
            .ContainerFromItem(Lv.SelectedItem);
        ((ListViewItem)Dep).Focus(); //2.step
    }

我很久以前就遇到过这个问题,现在我又被它困住了很长一段时间。在您的特定控件类型上启动任何 MessageBox 或展开或下拉,其中任何一个都可以完成工作并启动 ItemContainerGenerator。然而,在 .Focus() 之前,.UpdateLayout() 是正确的做法。应该类似于 Treeview 或其项目之一。