在Windows 8.1商店XAML中添加新项后,ListView.ContainerFromItem返回null

4

我有一个简单的ListView,没有项目模板,并设置了SelectionChanged事件:

    <ListView x:Name="list1" HorizontalAlignment="Left"
              Height="556"
              Margin="209,93,0,0"
              VerticalAlignment="Top"
              Width="1033"
              SelectionChanged="list1_SelectionChanged" />

    private void list1_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {            
        var container = (sender as ListView).ContainerFromItem(e.AddedItems.First());
        var presenter = VisualTreeHelper.GetChild(container, 0);            
    }

我也有一个如下的测试类:

class Test
{
    public string FirstName { get; set; }
    public string Surname { get; set; }
}

现在在页面的构造函数中,我有以下代码来创建一个Test项目的ObservableCollection,添加一些项目,然后将其设置为ListView的ItemSource:
ObservableCollection<Test> testCollection;

public MainPage()
{
    this.InitializeComponent();

    testCollection = new ObservableCollection<Test>();
    testCollection.Add(new Test { FirstName = "Bob", Surname = "Smith1" });
    testCollection.Add(new Test { FirstName = "Bob", Surname = "Smith2" });
    testCollection.Add(new Test { FirstName = "Bob", Surname = "Smith3" });

    list1.ItemsSource = testCollection;
}

现在当我在列表中选择一个项目并触发SelectionChanged事件时,container变量如预期一样包含ListViewItem。
现在,在表单上我也有一个按钮,在这里是按钮点击事件:
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        testCollection.Add(new Test { FirstName = "Bob", Surname = "Smith4" });

        list1.SelectedIndex = list1.Items.Count - 1;
    }

最后一行代码选择了新的项,触发了SelectionChanged事件,但是这次container变量为null。请问有人能告诉我为什么会出现这种情况,以及我该如何解决?

谢谢!


我认为你试图在“ListView”创建之前访问项目容器。你必须等到项目加载和处理完毕后再进行访问。尝试的一个选项是将列表的“SelectedIndex”或“SelectedItem”绑定到“ViewModel”中的值。我不确定它是否百分之百可行,但我相信它会起作用。 - Nate Diamond
此外,如果列表是虚拟化的(通常是这样),你可能需要等待很长时间,直到它被滚动到视图中才能获取容器。 - Filip Skakun
这对我有效:(sender as ListView).ContainerFromItem(e.AddedItems.First()) as ListViewItem - Amir
1个回答

10

我可以告诉你。

你在这里遇到的问题是时间上的问题。你认为你可以多快地向集合中添加一个项目?很快——只需要一两毫秒。你认为ListView设置SelectedItem属性并引发SelectionChanged事件需要多快?也很快——一两毫秒。但是,ListView实际上渲染新项目并生成容器需要多长时间?很长——大约需要10到100毫秒,具体取决于DataTemplate的复杂程度。

我知道你的测试没有设置ItemTemplate,我猜你这样做是认为它会更快——也许是即时的。但是在每个ItemsControl中都有一个默认的DataTemplate。即使是内置的简单DataTemplate,仍然比你提升一行C#代码所需的时间要长。

以下代码将说明我的意思:

async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    var items = Enumerable.Range(1, 10)
        .Select(x => new Item { FirstName = "Bob", LastName = "Smith" + x.ToString() });
    var list = new ObservableCollection<Item>(items);
    MyListView.ItemsSource = list;
    var item = new Item { FirstName = "Jerry", LastName = "Nixon" };
    list.Add(item);
    await Task.Delay(1000);
    MyListView.SelectedItem = item;
}

private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    System.Diagnostics.Debug.Assert(e.AddedItems.Any());
    var listview = sender as ListView;
    var container = listview.ContainerFromItem(e.AddedItems.First());
    System.Diagnostics.Debug.Assert(container != null);
    var presenter = VisualTreeHelper.GetChild(container, 0);
    System.Diagnostics.Debug.Assert(presenter != null);
}
请注意Delay代码,这是使其工作的原因。希望你已经简化了你的问题,这就是为什么你在代码后端而不是在视图模型中进行工作的原因。好的。首先,请确保在Loaded中处理此类逻辑,以便您可以确信您的ListView可用。然后,再在您的视图模型中进行处理。
所以,我回答了你的问题。为什么?因为时间紧迫。
在我看来,这一切可能都可以通过一个简单的视图模型解决,但是,我只了解到了你这里的一点情况。也许有些事情我没有看到。
祝你好运!

我刚刚花了大约8个小时来找你的解决方案。非常感谢。Windows商店有趣的文档。 - Amir
@Amir Windows 真是太有趣了... 真的很让人头疼。 - visc
刚刚在Windows 10遇到了这个问题。这里仍然适用相同的解决方案。 - Michael Kintscher they-them

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接