简体   繁体   English

在不丢失SelectedItem的情况下替换ListView的ItemsSource

[英]Replacing a ListView's ItemsSource Without Loosing SelectedItem

I'm data-binding a ListView to a collection that comes from a service layer. 我将ListView数据绑定到来自服务层的集合。 In response to events, the view model associated with the ListView refreshes the ListView's data. 响应事件,与​​ListView关联的视图模型刷新ListView的数据。 In order to retrieve updated data, the vm retrieves a new collection instance from the service layer. 为了检索更新的数据,vm从服务层检索一个新的集合实例。 Items in this collection are Equals() but not ReferenceEquals() to the equivalent items in the previously-returned collection. 此集合中的项目与先前返回的集合中的等效项目是Equals(),但不是ReferenceEquals()。

Can you recommend an approach that avoids the downsides of the approaches below while still allowing the vm to plug this data into the ListView without causing the ListView to lose its SelectedItem? 您是否可以推荐一种避免以下方法的缺点,同时又允许虚拟机将此数据插入到ListView中而不导致ListView丢失其SelectedItem的方法?

Thank you, 谢谢,
Ben


A simple approach (view model code; ListView's SelectedItem & ItemsSource are bound to identically-bound properties on the vm): 一种简单的方法(视图模型代码; ListView的SelectedItem和ItemsSource绑定到vm上相同绑定的属性):

var selectedItem = SelectedItem;
ItemsSource = service.GetData();
SelectedItem = Accounts.SingleOrDefault(x => x.Equals(selectedItem));

This approach seems ugly. 这种方法看起来很丑。 It also involves SelectedItem being reset--a potential problem if changing the SelectedItem changes the detail edit from on a master-detail form. 它还涉及到重设SelectedItem-如果更改SelectedItem会更改主详细信息表单上的明细编辑,则可能会出现问题。 (The setting of ItemsSource results in SelectedItem being cleared which is why it's re-set on the last line.) (ItemsSource的设置会导致SelectedItem被清除,这就是为什么在最后一行将其重置的原因。)

Another approach is writing an adapter that loads an ObservableCollection<> with proxy objects pointing to the data returned from the initial service layer call. 另一种方法是编写一个适配器,该适配器使用指向从初始服务层调用返回的数据的代理对象加载ObservableCollection <>。 Any time updated data is retrieved from the service layer, the proxy objects are updated to point to the newly-retrieved objects. 每当从服务层检索更新的数据时,代理对象都会更新以指向新检索的对象。 This way, the ListView's ItemsSource does not need to be reset to update the ListView (it stays bound to the same ObservableCollection<>) which means that SelectedItem won't be reset. 这样,无需重置ListView的ItemsSource即可更新ListView(它始终绑定到相同的ObservableCollection <>),这意味着不会重置SelectedItem。 A downside to this approach is the amount of code involved. 这种方法的缺点是涉及的代码量很大。

hmm... how about that: 嗯...那呢:

Register event handlers for SelectionChanged and DataContextChanged on the ListView: 在ListView上为SelectionChanged和DataContextChanged注册事件处理程序:

<ListView Name="listView" SelectionChanged="listView_SelectionChanged" DataContextChanged="listView_DataContextChanged" />

In the SelectionChanged handler save the SelectedItem in the Tag of the ListView 在SelectionChanged处理程序中,将SelectedItem保存在ListView的Tag中

private void listView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    (sender as ListView).Tag = (sender as ListView).SelectedItem;
}

Once the DataContext for the ListView changed, evaluate the last SelectedItem that was saved in the Tag against the new collection and set the new item from that collection: 一旦ListView的DataContext更改,就针对新集合评估保存在Tag中的最后一个SelectedItem,并从该集合中设置新项目:

    private void listView_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        List<CustomClass> newList = e.NewValue as List<CustomClass>;
        if (newList != null && (sender as ListView).Tag != null)
        {
            foreach (CustomClass cClass in newList)
                if (cClass.Equals((sender as ListView).Tag as CustomClass))
                    (sender as ListView).SelectedItem = cClass;
        }
    }

I ended up writing mapper code that loaded an ObservableCollection with proxy objects pointing to the real objects retrieved from the service layer. 我最后编写了映射器代码,该映射器代码加载了ObservableCollection,其中的代理对象指向从服务层检索到的真实对象。 Each time an update was fetched from the service layer, the mapper code would add/remove/update the proxy objects, as appropriate. 每次从服务层获取更新时,映射器代码都会酌情添加/删除/更新代理对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM