CollectionViewSource,如何筛选数据?

11
我正在将ComboBox绑定到实体,但我希望过滤数据。
迄今为止,我已经尝试了两种方法: 1. "简单"一点:通过LINQ直接将过滤器应用于ObjectSet to Entities 2. 设置过滤事件处理程序,如msdn中所描述的那样。
我对第一种方法感到满意,特别是因为生成到数据库的查询包含一个WHERE子句,因此不需要从远程数据库检索整个数据......
然而,如果在运行时我想更改所应用的过滤器,则第二种方法要灵活得多。我遵循了msdn上的示例,但我出现了异常,为什么?
所以,我的问题是: 1. 哪种方法更好? 2. 为什么我会出现异常?
这是我的代码:
 private void UserControl_Loaded(object sender, RoutedEventArgs e)
    {
        //Do not load your data at design time.
        if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
        {
            //Load your data here and assign the result to the CollectionViewSource.
            System.Windows.Data.CollectionViewSource myCollectionViewSource =
                (System.Windows.Data.CollectionViewSource)
                this.Resources["tSCHEDEViewSource"];

            // If I use this I get the data filtered on startup, but is it the right mode?
            //myCollectionViewSource.Source = _context.TSCHEDE.Where(s => s.KLINEA == kLinea && s.FCANC == "T").OrderBy(s => s.DSCHEDA).OrderByDescending(s => s.DSTORICO);

            // Instead If I apply my custom filtering logic
            myCollectionViewSource.Filter += new FilterEventHandler(filterSource);

            myCollectionViewSource.Source = _context.TSCHEDE; // ... Here i get an exception: 
            // 'System.Windows.Data.BindingListCollectionView' view does not support filtering. ???
        }
    }


    private void filterSource(object sender, FilterEventArgs e)
    {
        TSCHEDE scheda = e.Item as TSCHEDE;
        if (scheda != null)
        {
            if (scheda.KLINEA == 990)
            {
                e.Accepted = true;
            }
            else
            {
                e.Accepted = false;
            }
        }
    }

编辑: 我已经尝试在视图上实现Filter属性而不是设置EventHandler:

myCollectionView = (BindingListCollectionView)myCollectionViewSource.View;
myCollectionView.Filter = new Predicate<object>(Contains);

public bool Contains(object de)
    {
        TSCHEDE scheda = de as TSCHEDE;
        return (scheda.KLINEA == 990);
    }

现在我得到一个不太有用的异常:

System.NotSupportedException: 指定的方法不受支持。 at System.Windows.Data.CollectionView.set_Filter(Predicate`1 value)

编辑

XAML代码:

<UserControl.Resources>
    <CollectionViewSource x:Key="tSCHEDEViewSource" d:DesignSource="{d:DesignInstance my:TSCHEDE, CreateList=True}"  >
    </CollectionViewSource>
    <DataTemplate x:Key="SchedaTemplate">
        <StackPanel Orientation="Horizontal" >
            <TextBlock Text="{Binding Path=KSCHEDA}" Width="60"></TextBlock>
            <TextBlock Text="{Binding Path=DArticolo}" Width="200"></TextBlock>
            <TextBlock Text=" - " Width="40"></TextBlock>
            <TextBlock Text="{Binding Path=DSTORICO}" Width="150"></TextBlock>
        </StackPanel>
    </DataTemplate>
</UserControl.Resources>
<Grid Background="PapayaWhip" DataContext="{StaticResource tSCHEDEViewSource}" DataContextChanged="StartHere" Name="rootGrid">
    <ComboBox ItemTemplate="{StaticResource SchedaTemplate}" Grid.Column="1" Grid.Row="1" Height="23" HorizontalAlignment="Left" ItemsSource="{Binding}" Margin="23,129,0,0" Name="tSCHEDEComboBox1" SelectedValuePath="KSCHEDA" VerticalAlignment="Top" Width="393">
        <ComboBox.ItemsPanel>
            <ItemsPanelTemplate>
                <VirtualizingStackPanel />
            </ItemsPanelTemplate>
        </ComboBox.ItemsPanel>
    </ComboBox>
</Grid>

现在我认为问题出在XAML绑定上,而不是代码后台...

3个回答

11

请查看

1) CollectionView 过滤

过滤需要一个委托 (Predicate),根据它来进行过滤。Predicate 接受一个项目,根据返回的 true 或 false 值,选择或取消选择一个元素。

this.Source.Filter = item => {
    ViewItem vitem = item as ViewItem;
    return vItem != null && vitem.Name.Contains("A");
};

2) 动态过滤数据


我已经按照你给的第一个链接尝试了一下,但是后来事情变得有点复杂了... 最重要的是,我从myCollectionViewSource.View中获取的是一个ICollectionView,它似乎不支持排序或过滤(CanSort和CanFilter属性为false)。在XAML中,我有: <CollectionViewSource x:Key="tSCHEDEViewSource" d:DesignSource="{d:DesignInstance my:TSCHEDE, CreateList=True}" /> - spiderman
3
程序员天生就要做复杂的事情 :-) - Master Stroke
1
这实际上就是答案。 - Shaun Wilson
除了第一个链接外,WPF始终绑定到视图而不是集合,这是默认视图。由于默认视图由所有绑定共享,因此可以在视图上进行过滤或排序,并自动显示。如此解释:CollectionViewSource.GetDefaultView - royalTS

7

我终于找到了一种解决方法,就像 这个问题中所发表的那样,需要显式声明集合的类型:

CollectionViewType="ListCollectionView"

因此,在XAML中添加了集合类型:

<CollectionViewSource x:Key="tSCHEDEViewSource" d:DesignSource="{d:DesignInstance my:TSCHEDE,  CreateList=True}" CollectionViewType="ListCollectionView">
    </CollectionViewSource>

现在,在代码中事件处理程序已经起作用了:
myCollectionViewSource.Filter += new FilterEventHandler(filterSource);

唯一的遗憾是我不明白,为什么对于一个看起来如此简单的东西,我却要在XAML中“手动”强制它? 对我来说,这似乎是一种黑客行为,而且非常容易出错...


14
有没有人在不留言的情况下给你点踩,是不是让你很烦? - Warlord 099
可能是他们收到了这个错误 - CollectionViewType 属性只能在初始化期间设置。 - turkinator
@Warlord099 这很奇怪。我们从来没有抱怨过赞……只是一种想法。但是,我们几乎确定这是因为评论/帖子帮助了他人。 - Yatin
3
如果一个答案已经是正确的/有帮助的,那么可能没有什么其他的东西能够增加好处。但是,如果一个答案不正确/没有帮助,那么解释为什么会始终有益。无论如何,现在这个被接受的答案尽管在两年前我发表初始评论时遭到多次否定。 - Warlord 099
1
如果你遇到了“CollectionViewType属性只能在初始化期间设置”的错误,只需在XAML中将CollectionViewType作为第一个属性设置,然后再设置其他属性。我知道这个答案很老了,但也许对某些人有帮助。 - Unknown Coder

0
<TextBox x:Name="FilterTextBox">
    <b:Interaction.Triggers>
        <b:EventTrigger EventName="TextChanged">
            <b:CallMethodAction
                MethodName="Refresh"
                TargetObject="{Binding View, BindsDirectlyToSource=True, Source={StaticResource tSCHEDEViewSource}}" />
        </b:EventTrigger>
    </b:Interaction.Triggers>
</TextBox>

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