I have a WPF ListView control, ItemsSource is set to an ICollectionView created this way:
var collectionView =
System.Windows.Data.CollectionViewSource.Ge
It looks like this is due to a sort of known but not-well-described problematic behavior with ListView (and maybe some other WPF controls). It requires that an app call Focus() on the particular ListViewItem, after programmatically setting the SelectedItem.
But the SelectedItem itself is not a UIElement. It's an item of whatever you are displaying in the ListView, often a custom type. Therefore you cannot call this.listView1.SelectedItem.Focus(). That's not gonna work. You need to get the UIElement (or Control) that displays that particular item. There's a dark corner of the WPF interface called ItemContainerGenerator, which supposedly lets you get the control that displays a particular item in a ListView.
Something like this:
this.listView1.SelectedItem = thing;
// *** WILL NOT WORK!
((UIElement)this.listView1.ItemContainerGenerator.ContainerFromItem(thing)).Focus();
But there's also a second problem with that - it doesn't work right after setting the SelectedItem. ItemContainerGenerator.ContainerFromItem() always seems to return null. Elsewhere in the googlespace people have reported it as returning null with GroupStyle set. But it exhibited this behavior with me, without grouping.
ItemContainerGenerator.ContainerFromItem() is returning null for all objects being displayed in the list. Also ItemContainerGenerator.ContainerFromIndex() returns null for all indicies. What's necessary is to call those things only after the ListView has been rendered (or something).
I tried doing this directly via Dispatcher.BeginInvoke() but that does not work either.
At the suggestion of some other threads, I used Dispatcher.BeginInvoke() from within the StatusChanged event on the ItemContainerGenerator. Yeah, simple huh? (Not)
Here's what the code looks like.
MyComplexType current;
private void SelectThisItem(string value)
{
foreach (var item in collectionView) // for the ListView in question
{
var thing = item as MyComplexType;
if (thing.StringProperty == value)
{
this.listView1.ItemContainerGenerator.StatusChanged += icg_StatusChanged;
this.listView1.SelectedItem = thing;
current = thing;
return;
}
}
}
void icg_StatusChanged(object sender, EventArgs e)
{
if (this.listView1.ItemContainerGenerator.Status
== System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
{
this.listView1.ItemContainerGenerator.StatusChanged
-= icg_StatusChanged;
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input,
new Action(()=> {
var uielt = (UIElement)this.listView1.ItemContainerGenerator.ContainerFromItem(current);
uielt.Focus();}));
}
}
That's some ugly code. But, programmatically setting the SelectedItem this way allows subsequent arrow navigation to work in the ListView.