Che*_*eso 10 wpf listview observablecollection icollectionview
我有一个WPF ListView控件,ItemsSource设置为以这种方式创建的ICollectionView:
var collectionView = 
  System.Windows.Data.CollectionViewSource.GetDefaultView(observableCollection);
this.listView1.ItemsSource = collectionView;
...其中observableCollection是复杂类型的ObservableCollection.ListView配置为为每个项目显示复杂类型上的一个字符串属性.
用户可以刷新ListView,此时我的逻辑存储当前所选项的"键字符串",重新填充底层的observableCollection.然后将先前的排序和过滤器应用于collectionView.此时我想"重新选择"在刷新请求之前选择的项目.observableCollection中的项是新实例,因此我比较各自的字符串属性,然后选择匹配的字符串属性.像这样:
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项,并且用户按下F5,则重构列表,然后选择具有与前4项相同的字符串属性的项.有时这是新的第4项,有时不是,但它提供了" 最不惊讶的行为 ".
当用户随后使用箭头键在ListView中导航时,问题就出现了.刷新后的第一个向上或向下箭头会导致(新)列表视图中的第一个项目被选中,而不管前一个逻辑选择了哪个项目.任何进一步的箭头键按预期工作.
为什么会这样?
这显然违反了"最不惊讶"的规则.我怎么能避免呢?
编辑
 
进一步搜索,这似乎与未答复的
WPF ListView箭头导航和击键问题描述的相同异常,除了我提供更多细节.
Che*_*eso 15
看起来这是由于ListView(以及其他一些WPF控件)的某种已知但未充分描述的问题行为.Focus()在以编程方式设置SelectedItem之后,它需要在特定ListViewItem上调用app .
但是SelectedItem本身不是UIElement.它是ListView中显示的任何项目,通常是自定义类型.所以你不能打电话this.listView1.SelectedItem.Focus().那不行.您需要获取显示该特定项目的UIElement(或Control).WPF接口的一个黑暗角落叫做ItemContainerGenerator,它可以让你获得在ListView中显示特定项目的控件.  
像这样的东西:
this.listView1.SelectedItem = thing;
// *** WILL NOT WORK!
((UIElement)this.listView1.ItemContainerGenerator.ContainerFromItem(thing)).Focus();
但是还有第二个问题 - 它在设置SelectedItem后无法正常工作.ItemContainerGenerator.ContainerFromItem()似乎总是返回null.在googlespace的其他地方,人们已经报告它在GroupStyle设置时返回null.但它在没有分组的情况下表现出这种行为.
ItemContainerGenerator.ContainerFromItem()为列表中显示的所有对象返回null.ItemContainerGenerator.ContainerFromIndex()对所有指标也返回null.只有在ListView被渲染(或其他东西)之后才调用那些东西是必要的.
我尝试直接通过这样做,Dispatcher.BeginInvoke()但这也不起作用.
在一些其他线程的建议下,我Dispatcher.BeginInvoke()在StatusChanged事件中使用了ItemContainerGenerator.是的,简单吧?(不)
这是代码的样子.
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();}));
    }
}
这是一些丑陋的代码.但是,以这种方式以编程方式设置SelectedItem允许后续箭头导航在ListView中工作.
| 归档时间: | 
 | 
| 查看次数: | 5783 次 | 
| 最近记录: |