ICollectionView的SourceCollection为空。

12
我有一个ViewModel,其中包含两个ICollectionView,它们作为ItemsSource绑定到两个不同的ListBox。两个ListBox都使用不同的过滤器来封装相同的ObservableCollection。最初一切正常,两个ListBox都填充正确。
但是,当我更改ObservableCollection中的项并修改与过滤相关的属性时,ListBoxes不会更新。在调试器中,我发现两个ICollectionView的SourceCollection为空,虽然我的ObservableCollection仍然存在。
这是我如何修改项目以确保更新ICollectionViews,方法是删除并添加相同的项目:
private void UnassignTag(TagViewModel tag)
{
    TrackChangedTagOnCollectionViews(tag, t => t.IsAssigned = false);
}

private void TrackChangedTagOnCollectionViews(TagViewModel tag, Action<TagViewModel> changeTagAction)
{
    _tags.Remove(tag);

    changeTagAction.Invoke(tag);

    _tags.Add(tag);
}

这个机制在我使用相同类别的另一个上下文中也有效。
此外,我发现如果在ICollectionViews的CollectionChanged事件上注册侦听器,则问题会消失。 我确保从GUI线程创建和修改它们,并怀疑垃圾回收是问题所在,但目前我卡住了...有什么想法吗?
更新:
在调试时,我意识到在将我的UserControl托管到其中的WinForms表单上调用ShowDialog()之前,SourceCollections仍然存在。 当显示对话框时,它们消失了。
我像这样创建ICollectionViews:
AvailableTags = new CollectionViewSource { Source = _tags }.View;
AssignedTags = new CollectionViewSource { Source = _tags }.View;

这是我如何绑定其中一个(另一个也很相似)的方法:

<ListBox Grid.Column="0"  ItemsSource="{Binding AvailableTags}" Style="{StaticResource ListBoxStyle}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Border Style="{StaticResource ListBoxItemBorderStyle}">
                        <DockPanel>
                            <Button DockPanel.Dock="Right" ToolTip="Assign" Style="{StaticResource IconButtonStyle}"
                                            Command="{Binding Path=DataContext.AssignSelectedTagCommand, RelativeSource={RelativeSource AncestorType={x:Type tags:TagsListView}}}"
                                            CommandParameter="{Binding}">
                                <Image Source="..."/>
                            </Button>

                            <TextBlock Text="{Binding Name}" Style="{StaticResource TagNameTextBlockStyle}"/>
                        </DockPanel>
                    </Border>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

我在我的ViewModel中使用MvvmLight的RelayCommand<T>作为ICommand的实现:

AssignSelectedTagCommand = new RelayCommand<TagViewModel>(AssignTag);

1
请多发一些代码。 - Ayyappan Subramanian
@Sinatr:我怀疑这与此无关。对项目进行INotifyPropertyChanged应该不会影响过滤(正如我所写的:在不同的上下文中它是有效的)。我从这里得到了删除/添加的想法(http://drwpf.com/blog/2008/10/20/itemscontrol-e-is-for-editable-collection/)。 - EagleBeak
@Ganesh:你能具体说明一下吗?如果不在帖子上弄乱,我就不知道该发布哪个代码了。ObservableCollection在其他地方没有被修改过。ICollectionViews也是如此。 - EagleBeak
我想了解更多关于您的绑定,特别是在尝试更新集合方面的内容。 - Ayyappan Subramanian
我在TabControl中有两个TabItems的问题。每个项目都有自己的ICollectionView在同一个视图模型中。最初,SourceCollection被填充,但在加载第一个TabItem后,第二个TabItem的ICollectionView的SourceCollection为null。 - Michi-2142
2个回答

25

我也遇到了类似的问题。在更新底层集合时,我会在所有筛选视图上调用Refresh()。有时,这会导致从ListCollectionView.PrepareLocalArray()中抛出NullReferenceException,因为SourceCollection为空。

问题在于,你不应该绑定到CollectionView,而是应该绑定到CollectionViewSource.View属性。

这是我的做法:

public class ViewModel {

    // ...

    public ViewModel(ObservableCollection<ItemViewModel> items)
    {
        _source = new CollectionViewSource()
        {
            Source = items,
            IsLiveFilteringRequested = true,
            LiveFilteringProperties = { "FilterProperty" }
        };

        _source.Filter += (src, args) =>
        {
            args.Accepted = ((ItemViewModel) args.Item).FilterProperty == FilterField;
        };
    }

    // ...

    public ICollectionView View
    {
        get { return _source.View; }
    }

    // ...
}

哇?他正在做完全相同的事情:new CollectionViewSource { Source = _tags }.View; - Lukáš Koten
2
这并不完全相同。CollectionViewSource.View是一个属性,其值会发生变化。 - sconzey
什么?我尝试了你的方法,它确实有效,但我完全不明白为什么... 很抱歉给你投了反对票,这应该是被采纳的答案! - Lukáš Koten
3
宇宙受我们自己理解的限制。 ;) 我认为发生的事情是,在.View属性引用的对象底层被改变了,而且.View属性本身也会改变。我不确定这如何与WPF数据绑定交互,但如果你使用.View.Refresh(),它肯定会产生影响。总之,很高兴能帮助你解决问题。 - sconzey
请参考Chris Cannell的答案和评论,以获取更详细的解释。您实际上不需要每次从CollectionViewSource中获取View。您只需要确保垃圾收集器不会触及CollectionViewSource即可,而此代码通过在ViewModel中存储引用来实现此目的。 - Daniel

16
你遇到问题的原因是 CollectionViewSource 被垃圾回收了。
如果你只保留了对 ICollectionView 的引用,并在从中获取视图后让 CollectionViewSource 超出了作用域,那么你可能会遇到这个问题。请保留对 CollectionViewSource 的引用。

1
这帮了我。我一直只是保留对ICollectionView的引用,并在获取视图后让CollectionViewSource超出范围。看到这个提示后,我保留了对CollectionViewSource的引用,现在一切都正常工作。 - Zach Mierzejewski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接