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

Sun*_*Sun 3 xaml winrt-xaml windows-store-apps 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);            
    }
Run Code Online (Sandbox Code Playgroud)

我还有一个测试类如下:

class Test
{
    public string FirstName { get; set; }
    public string Surname { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

现在在页面的构造函数中,我有以下代码创建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;
}
Run Code Online (Sandbox Code Playgroud)

现在,当我在列表中选择一个项目并激活SelectionChanged事件时,容器变量按预期包含ListViewItem.

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

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

        list1.SelectedIndex = list1.Items.Count - 1;
    }
Run Code Online (Sandbox Code Playgroud)

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

任何人都可以告诉我为什么这是空的,我该如何绕过这个?

谢谢

Jer*_*xon 10

我可以告诉你.

你在这里遇到的是时间问题.您认为可以多快将项目添加到集合中?快 - 只有一两毫秒.您认为ListView设置SelectedItem房产和SelectionChanged举办活动需要多快?快 - 一毫秒或两秒.但是,您认为ListView在屏幕上实际渲染新项目并为其生成容器需要多快?很长一段时间 - 比如10到100毫秒,取决于复杂性DataTemplate.

我意识到你的测试没有设置ItemTemplate,我假设你这样做,假设它会渲染得更快 - 也许是即时的.但DataTemplate每个都有默认值ItemsControl.而且,即使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);
}
Run Code Online (Sandbox Code Playgroud)

请注意Delay代码; 这就是它的工作原理.

希望您简化了问题,这就是您在代码隐藏而不是在视图模型中进行工作的原因.精细.首先,确保并处理这种类型的逻辑,Loaded这样您就可以确信它ListView甚至可用.然后,再次在视图模型中执行此操作.

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

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

祝你好运!