I have a simple ListView with no item template and the SelectionChanged event setup:
<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);
}
I also have a test class as follows:
class Test
{
public string FirstName { get; set; }
public string Surname { get; set; }
}
Now in constructor for the page I have the following code that creates an ObservableCollection of the Test items, adds a few and then sets that as the ListView's 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;
}
Now when I select an item in the list and the SelectionChanged Event fires, the container variable contains the ListViewItem as expected.
Now, I have button on the form too, here is the button click event:
private void Button_Click(object sender, RoutedEventArgs e)
{
testCollection.Add(new Test { FirstName = "Bob", Surname = "Smith4" });
list1.SelectedIndex = list1.Items.Count - 1;
}
The last line selects the new item and the SelectionChanged event fires but this time the container variable is null.
Can anybody tell me why this is null and how do I get round this?
Thanks
I can tell you.
What you are experiencing here is a problem of timing. How fast do you think you can add an item to a collection? Fast - just a millisecond or two. How fast do you think it takes the ListView
to set the SelectedItem
property and raise the SelectionChanged
event? Fast - a millisecond or two. But, how fast do you think it takes the ListView
to actually render the new item on the screen and generate the container for it? A long time - like 10 to 100 milliseconds, depending on the complexity of the DataTemplate
.
I realize your test didn't set the ItemTemplate
, and I assume you did it that way assuming that it would render faster - maybe instant. But there is a default DataTemplate
in every ItemsControl
. And, even a simple DataTemplate
like the built-in one, still takes more time than it takes you to advance a single C# line.
This code will illustrate what I mean:
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);
}
Please notice the
Delay
code; this is what makes it work.
Hopefully, you simplified your question and that is why you are doing the work in code-behind and not in a view model. Fine. First, be sure and handle this type of logic in Loaded
so you can be confident that your ListView
is even available. Then again, do this in your view model.
So, I have answered your question. Why? Because of timing.
Seems to me it might all be solved with a simple view model, but then again, I only know a little bit of your situation here. Maybe there's something I am not seeing.
Best of luck!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With