箭头键不经过编程设置ListView.SelectedItem工作 [英] Arrow keys don't work after programmatically setting ListView.SelectedItem

查看:202
本文介绍了箭头键不经过编程设置ListView.SelectedItem工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个WPF ListView控件的ItemsSource设置为ICollectionView创建这样的:

I have a WPF ListView control, ItemsSource is set to an ICollectionView created this way:

var collectionView = 
  System.Windows.Data.CollectionViewSource.GetDefaultView(observableCollection);
this.listView1.ItemsSource = collectionView;

...其中的ObservableCollection是一个复杂的类型的一个ObservableCollection。在ListView被配置成显示,对于每个项目,在复数型只是一个字符串属性。

...where observableCollection is an ObservableCollection of a complex type. The ListView is configured to display, for each item, just one string property on the complex type.

用户可以刷新的ListView,此时我的逻辑存储密钥字符串为当前选择的项目,重新填充底层的ObservableCollection。然后previous排序和过滤器被应用到的CollectionView。在这一点上,我想重新选择已被选定请求刷新之前的项目。在的ObservableCollection的项目有新的情况,所以我比较各自的字符串属性,然后只需选择一个匹配。像这样的:

The user can refresh the ListView, at which point my logic stores the "key string" for the currently selected item, re-populates the underlying observableCollection. The previous sort and filter is then applied to the collectionView. At this point I'd like to "re select" the item that had been selected before the request to refresh. The items in the observableCollection are new instances, so I compare the respective string properties and then just select one that matches. Like this:

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.SelectedItem = thing;
            return;
        }
    }
}

这一切工作的。如果第4项目被选择,并且用户presses F5,则列表复原,然后用相同的字符串属性作为previous第四项目的项目被选中。有时,这是新的4项,有时候没有,但它提供了最小惊讶行为

This all works. If the 4th item is selected, and the user presses F5, then the list is reconstituted and then the item with the same string property as the previous 4th item gets selected. Sometimes this is the new 4th item, sometimes not, but it provides "least astonishment behavior".

问题是当用户随后使用箭头键在ListView中导航。刷新后的第一个向上或向下箭头会导致在(新)的ListView中的第一项被选中,不管已经由previous逻辑选择哪个项目。任何进一步的箭头键正常工作。

The problem comes when the user subsequently uses arrow keys to navigate through the ListView. The first up or down arrow after a refresh causes the first item in the (new) listview to be selected, regardless of which item had been selected by the previous logic. Any further arrow keys work as expected.

这是怎么回事?

这pretty明显违反了最小惊讶的规则。我怎样才能避免呢?

This pretty clearly violates the "least astonishment" rule. How can I avoid it?

修改结果
在进一步的搜索,这似乎是在未结果描述的相同异常
<一href=\"http://stackoverflow.com/questions/5944430/wpf-listview-arrow-navigation-and-keystroke-problem\">WPF ListView的箭头导航和击键的问题,但我提供更多的细节。

EDIT
Upon further search, this seems like the same anomaly described by the unanswered
WPF ListView arrow navigation and keystroke problem , except I provide more detail.

推荐答案

看起来这是由于<一个href=\"http://stackoverflow.com/questions/165424/how-does-itemcontainergenerator-containerfromitem-work-with-a-grouped-list/169123#169123\">a那种知而不-精心描述的ListView的(也许其他一些WPF控件)有问题的行为。它要求一个应用程序调用焦点()特定的ListViewItem的,经过编程设置selectedItem。

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.

不过的SelectedItem本身不是一个的UIElement。这是无论你在ListView中显示的项目,往往是一个自定义类型。因此,你不能叫 this.listView1.SelectedItem.Focus()。这是不是要去工作。你需要得到显示该特定项目的UIElement(或控制)。还有的WPF界面称为 ItemContainerGenerator ,据称可以让你获得控件显示一个黑暗的角落在ListView特定项目。

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.

事情是这样的:

this.listView1.SelectedItem = thing;
// *** WILL NOT WORK!
((UIElement)this.listView1.ItemContainerGenerator.ContainerFromItem(thing)).Focus();

但也有与第二个问题 - 它不设置selectedItem之后的工作。 ItemContainerGenerator.ContainerFromItem()似乎总是返回null。其他地方googlespace人都报告它作为返回null与GroupStyle集。但它表现出与我的这种行为,不进行分组。

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()终止返回null。此外 ItemContainerGenerator.ContainerFromIndex()返回null所有indicies。什么是必要的是调用后才ListView控件已经呈现(或其它)这些东西。

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).

我试图通过直接这样做Dispatcher.BeginInvoke()但这并不能工作。

I tried doing this directly via Dispatcher.BeginInvoke() but that does not work either.

在某些其他线程的建议,我用 Dispatcher.BeginInvoke() StatusChanged 事件中上在 ItemContainerGenerator 。是啊,简单吧? (不)

At the suggestion of some other threads, I used Dispatcher.BeginInvoke() from within the StatusChanged event on the ItemContainerGenerator. Yeah, simple huh? (Not)

这里的code样子。

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();}));

    }
}

这是一些丑陋的code。但是,编程设置selectedItem这种方式可以让后续的箭头导航在ListView工作。

That's some ugly code. But, programmatically setting the SelectedItem this way allows subsequent arrow navigation to work in the ListView.

这篇关于箭头键不经过编程设置ListView.SelectedItem工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆