在Windows 8.1存储XAML中添加新项目后,ListView.ContainerFromItem返回null

时间:2014-06-02 13:17:07

标签: xaml windows-store-apps winrt-xaml windows-8.1

我有一个简单的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; }
}

现在在页面的构造函数中,我有以下代码创建一个测试项的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事件时,容器变量按预期包含ListViewItem。

现在,我在表单上也有按钮,这里是按钮点击事件:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        testCollection.Add(new Test { FirstName = "Bob", Surname = "Smith4" });

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

最后一行选择新项目并触发SelectionChanged事件,但这次容器变量为null。

有人可以告诉我为什么这是空的,我该如何解决这个问题?

由于

1 个答案:

答案 0 :(得分:10)

我可以告诉你。

您在这里遇到的是时间问题。您认为可以多快将项目添加到集合中?快 - 只有一两毫秒。您认为ListView设置SelectedItem属性并引发SelectionChanged事件需要多快?快 - 一毫秒或两秒。但是,您认为ListView在屏幕上实际呈现新项目并为其生成容器需要多快?很长一段时间 - 比如10到100毫秒,具体取决于DataTemplate的复杂程度。

我意识到你的测试没有设置ItemTemplate,我假设你这样做是假设它会渲染得更快 - 也许是即时的。但每个DataTemplate都有一个默认ItemsControl。而且,即使像内置的DataTemplate这样的简单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); } ,仍然需要花费更多的时间来推进单个C#线。

这段代码将说明我的意思:

Delay
  

请注意Loaded代码;这就是它的工作原理。

希望您简化了问题,这就是您在代码隐藏中而不是在视图模型中进行工作的原因。精细。首先,请确保在ListView中处理此类逻辑,以确保您的{{1}}可用。然后,再次在视图模型中执行此操作。

所以,我已经回答了你的问题。为什么?因为时间安排。

在我看来,这可能都是通过一个简单的视图模型来解决的,但话说回来,我只知道你的情况。也许有一些我没有看到的东西。

祝你好运!