wpf 使用 LinQ 过滤 ObservableCollection

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30007961/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 13:07:14  来源:igfitidea点击:

Using LinQ to filter ObservableCollection

c#wpflinqmvvmobservablecollection

提问by Dracke

I have a MVVM application and I am trying to make filtering through LinQ work on my ObservableCollection that is gotten from database based on Entity Framework.

我有一个 MVVM 应用程序,我试图通过 LinQ 对我的 ObservableCollection 进行过滤,该集合是从基于实体框架的数据库中获取的。

In View Model I have this:

在视图模型中,我有这个:

public class MenuListViewModel : BaseViewModelCollection<Menu>

{
    private string filterString;

    public string FilterString
    {
        get { return filterString; }
        set
        {
            if (Equals(value, filterString)) return;
            filterString = value;
            RaisePropertyChanged();
        }
    }

    //TODO problems with notification, filter doesn't work
    public ObservableCollection<Menu> FilteredItems
    {
        get
        {
            if (filterString == null) return Items; //Items is Observable Collection that contains every Item
            var query = Items.Where(x => x.Time.ToString().StartsWith(filterString));
            return new ObservableCollection<Menu>(query);
        }
    }

    public MenuListViewModel(MenuService menuService)
    {
        base.Service = menuService; //Using IoC to get service
    }
}

In Xaml I have the following Binding:

在 Xaml 中,我有以下绑定:

 <TextBox x:Name="RecipeFilterBox" Margin="5,5,0,0" TextWrapping="Wrap" Text="{Binding FilterString, NotifyOnTargetUpdated=True}" Grid.Column="1" Height="47.07" VerticalAlignment="Top"/>

The thing is that when I write anything in the TextBox, nothing changes. I know that there is something wrong with the propertyChanged event, but I really can't figure out how to fix this. If you need any more information about this app, just ask me.

问题是当我在 TextBox 中写任何东西时,没有任何变化。我知道 propertyChanged 事件有问题,但我真的不知道如何解决这个问题。如果您需要有关此应用程序的更多信息,请询问我。

EDIT: The xaml for FilteredItems looks like this:

编辑: FilteredItems 的 xaml 如下所示:

    <ListBox x:Name="MenuItemsListView" ItemsSource="{Binding FilteredItems}" SelectedItem="{Binding DeletedItem, Mode=OneWayToSource}" Foreground="#FFFFEDD3" FontFamily="Segoe Print" FontWeight="Bold" FontSize="18.667" Grid.ColumnSpan="3" Grid.Row="1" ItemContainerStyle="{DynamicResource ListBoxItemStyle1}" Style="{DynamicResource ListBoxStyle1}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Recipe.Name}" Width="255"/>
                    <TextBlock Width="175" Text="{Binding Time, Converter={StaticResource EnumTimeToItsDescriptionValueConverter}, Mode=OneWay}" />
                    <TextBlock Text="{Binding Date, StringFormat=dd.MM.yyyy}"/>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

回答by Omri Btian

you can achieve this using ICollectionView.

您可以使用ICollectionView.

use FilteredItemsas the underlying source of the ICollectionViewand expose ICollectionViewto your view instead of ObservableCollection<Menu>

使用FilteredItems如的底层源ICollectionView和揭露ICollectionView你的看法,而不是ObservableCollection<Menu>

Use the filter delegate to provide the filter logic

使用过滤器委托提供过滤器逻辑

FilteredItems.Filter = item =>
{
    Menu m = item as Menu;
    return m.Time.ToString().StartsWith(FilterString);
}

and when FilterStringchanges invoke FilterItems.Refresh();

FilterString更改调用时FilterItems.Refresh();

Here is an example:

下面是一个例子:

public class MenuListViewModel : BaseViewModelCollection<Menu>
{
   public MenuListViewModel()
   {
      var data = new List<Menu> { some data ... }; // your real list of menus
      // initialize the collection view
      FilteredItems = CollectionViewSource.GetDefaultView(data);
      // apply filtering delegate
      FilteredItems.Filter = i =>
      {
         // This will be invoked for every item in the underlying collection 
         // every time Refresh is invoked
         if (string.IsNullOrEmpty(FilterString)) return true;
         Menu m = i as Menu;
         return m.Time.ToString().StartsWith(FilterString);
      };
   }

   private string filterString;
   public string FilterString
   {
       get { return filterString; }
       set
       {
           if (Equals(value, filterString)) return;
           filterString = value;
           FilteredItems.Refresh(); // tirggers filtering logic
           RaisePropertyChanged("FilterString"); 
       }
   }

    public ICollectionView FilteredItems { get; set; }
}

You would also have to change the UpdateSourceTriggeron your filter TextBoxto make it update the FilterStringevery time the user changes the text.

您还必须更改UpdateSourceTrigger过滤器上的 ,TextBox以使其在FilterString每次用户更改文本时更新。

Text="{Binding FilterString, UpdateSourceTrigger=PropertyChanged, ...}

回答by fex

Add RaisePropertyChanged("FilteredItems")inside FilterStringsetter. FilteredItemsproperty changed is never raised so bindings doesn't work the way you expect.

添加RaisePropertyChanged("FilteredItems")内部FilterString二传手。FilteredItems从未引发更改的属性,因此绑定不会按您期望的方式工作。