如何在实体集合上提供动态过滤?

时间:2010-08-25 14:23:33

标签: wpf entity-framework filtering observablecollection

我有一个继承ObservableCollection的类ShipmentsCollection,它包含装运对象(实体)。这将显示在ShipmentsView UserControl的列表框中。我的意图是允许用户键入列表上方的文本框,并使用包含该字符串的项目过滤列表,以及基于多个复选框和基于单选按钮的选项(交付状态和订单方向)进行过滤。

我尝试过这几种方法,但似乎没有一种优雅或功能真实。我尝试过的事情如下:

  • 将ShipmentsCollection放入CollectionViewSource并通过谓词进行过滤。无法根据用户输入或选项更改找出使过滤器自动更新的好方法。
  • 重构为继承collectionViewSource的类,并尝试直接在XAML中声明但出现以下错误:“在配置中找不到指定的命名连接,不打算与EntityClient提供程序一起使用,或者无效” 。尝试修复,但找不到有效的解决方案。
  • 重构为继承自CollectionView,在代码隐藏中的事件处理程序中实现过滤器逻辑。仍然试图找出如何在不命名filtertext文本框控件的情况下将过滤器字符串放到事件处理程序中。

任何人都有一些关于在MVVM设计模式中实现此功能的好主意。我希望列表中最多有200个对象,因此它不会是一个巨大的过滤操作。

科里

2 个答案:

答案 0 :(得分:3)

你的第一个选择是我建议的那个。为了让自动过滤器基于键入工作,我会在ViewModel中执行类似SearchString属性的操作,将文本框文本绑定到该文本框,并将绑定中的UpdateSourceTrigger设置为PropertyChanged,以便每次调用SearchString PropertyChanged事件时间键入键而不是等到盒子失去焦点。

XAML:

<TextBox Text="{Binding SearchString, UpdateSourceTrigger=PropertyChanged}" />

ViewModel:将上面的属性设置为PropertyChanged,只要键入一个键,就会调用“Set”方法,而不是在文本框失去焦点时调用。

private string _searchString;
public string SearchString
{
    get { return _searchString; }
    set
    {
        if (_searchString != value)
        {
            _searchString = value;
            OnPropertyChanged("SearchString");
        }
    }
}

答案 1 :(得分:3)

我知道这个问题是封闭的,旧的。但对于像我这样搜索动态过滤的人,可以参考以下链接

https://github.com/lokeshlal/WPFDynamicFilters

上面的示例基于在实体模型的属性上定义的属性为每个实体创建过滤器。

举个例子:

  1. 定义过滤器的属性

    public class FilterAttribute : Attribute
    {
        public FilterAttribute() { }
        public string FilterLabel { get; set; }
        public object FilterValue { get; set; }
        public string FilterKey { get; set; }
        public Type FilterDataType { get; set; }
        public bool IsDropDown { get; set; }
        public string DropDownList { get; set; }
        public List<object> ObjectDropDownList { get; set; }
    }
    
  2. 在模型属性中应用以上属性

    public class GridModel
    {
        [Filter(FilterLabel = "Id",
            FilterKey = "Id",
            IsDropDown = false,
            FilterDataType = typeof(int))]
        public int Id { get; set; }
        [Filter(FilterLabel = "Name",
            FilterKey = "Name",
            IsDropDown = false,
            FilterDataType = typeof(string))]
        public string Name { get; set; }
        [Filter(FilterLabel = "Country",
            FilterKey = "Country",
            IsDropDown = true,
            FilterDataType = typeof(int),
            DropDownList = "Country")]
        public string Country { get; set; }
        [Filter(FilterLabel = "Address",
            FilterKey = "Address",
            IsDropDown = false,
            FilterDataType = typeof(string))]
        public string Address { get; set; }
    }
    
  3. 定义将绑定到下拉类型的模型

    public class Country
    {
        public int Id { get; set; } // id will be used for value
        public string Name { get; set; } // Name will be used for display value
    }
    
  4. 实际视图的ViewModel

    public class FilterViewModel
    {
        public ICommand CheckFiltersCommand { get; set; }
        public FilterViewModel()
        {
            CheckFiltersCommand = new DelegateCommand(GetFilters);
            GridSource = new List<GridModel>();
            GridSource.Add(new GridModel() { Id = 1, Name = "Name1", Country = "Denmark" });
            GridSource.Add(new GridModel() { Id = 2, Name = "Name2", Country = "India" });
            GridSource.Add(new GridModel() { Id = 3, Name = "Name3", Country = "Australia" });
            GridSource.Add(new GridModel() { Id = 4, Name = "Name4", Country = "India" });
            GridSource.Add(new GridModel() { Id = 5, Name = "Name5", Country = "Australia" });
            GridSource.Add(new GridModel() { Id = 6, Name = "Name6", Country = "Hongkong" });
    
            FilterControlViewModel = new FilterControlViewModel();
            FilterControlViewModel.FilterDetails = new List<FilterAttribute>();
    
            foreach (var property in typeof(GridModel).GetProperties())
            {
                if (property.GetCustomAttributes(true).Where(attr => attr.GetType() == typeof(FilterAttribute)).Any())
                {
                    var attribute = (FilterAttribute)property.GetCustomAttributes(true).Where(attr => attr.GetType() == typeof(FilterAttribute)).First();
                    FilterControlViewModel.FilterDetails.Add(attribute);
                }
            }
        }
    
        private void GetFilters()
        {
            FilterCollection = new Dictionary<string, object>();
            foreach (var filter in FilterControlViewModel.FilterDetails)
            {
                if (filter.IsDropDown)
                {
                    if (filter.FilterValue != null)
                        FilterCollection.Add(filter.FilterKey, filter.FilterValue.GetType().GetProperty("Id").GetValue(filter.FilterValue));
                }
                else
                {
                    FilterCollection.Add(filter.FilterKey, filter.FilterValue);
                }
            }
    
            MessageBox.Show(string.Join(", ", FilterCollection.Select(m => m.Key + ":" + Convert.ToString(m.Value)).ToArray()));
        }
    
        public List<GridModel> GridSource { get; set; }
    
        public Dictionary<string, object> FilterCollection { get; set; }
    
        public FilterControlViewModel FilterControlViewModel { get; set; }
    }
    
  5. 在上面的视图模型中,“FilterControlViewModel”属性将迭代模型的所有属性并收集属性的过滤器信息。 该属性将分配给用户控件,如下面的xaml文件中所述

        <Window x:Class="WPFDynamicFilters.GridWithFilters"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:local="clr-namespace:WPFDynamicFilters"
                mc:Ignorable="d"
                Title="gridwithfilters" Height="481.239" Width="858.171">
            <Grid>
                <Grid HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" x:Name="FilterGrid" Height="209" Width="830">
                    <Border BorderThickness="1" BorderBrush="Gold"/>
                    <local:Filter DataContext="{Binding FilterControlViewModel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,10" />
                </Grid>
                <DataGrid x:Name="DataGrid" ItemsSource="{Binding GridSource}" HorizontalAlignment="Left" Margin="10,294,0,0" VerticalAlignment="Top" Height="146" Width="830"/>
                <Button x:Name="button" Content="Check Filters" HorizontalAlignment="Left" Margin="10,245,0,0" VerticalAlignment="Top" Width="110" Command="{Binding CheckFiltersCommand}"/>
            </Grid>
        </Window>
    

    过滤器控件将获取所有属性并使用itemscontrol

    呈现控件
        <UserControl x:Class="WPFDynamicFilters.Filter"
                     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                     xmlns:local="clr-namespace:WPFDynamicFilters"
                     mc:Ignorable="d" 
                     d:DesignHeight="40" 
                     >
            <UserControl.Resources>
                <DataTemplate x:Key="TStringTemplate">
                    <StackPanel FlowDirection="LeftToRight">
                        <TextBlock Text="{Binding FilterKey}" />
                        <TextBox x:Name="TxtFieldValue" 
                             Text="{Binding FilterValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"   
                             RenderTransformOrigin="-9.3,0.5" Width="200" FontSize="16" TextAlignment="Left" VerticalAlignment="Center"/>
                    </StackPanel>
                </DataTemplate>
                <DataTemplate x:Key="TIntegerTemplate">
                    <StackPanel FlowDirection="LeftToRight">
                        <TextBlock Text="{Binding FilterKey}" />
                        <TextBox x:Name="IntFieldValue" 
                             Text="{Binding FilterValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"   
                             RenderTransformOrigin="-9.3,0.5" Width="200" FontSize="16" TextAlignment="Left" VerticalAlignment="Center"/>
                    </StackPanel>
                </DataTemplate>
                <DataTemplate x:Key="TDropDownTemplate">
                    <StackPanel FlowDirection="LeftToRight">
                        <TextBlock Text="{Binding FilterKey}" />
                        <ComboBox 
                        SelectedItem="{Binding FilterValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                              SelectedIndex="{Binding FilterValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                              ItemsSource="{Binding ObjectDropDownList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                              DisplayMemberPath="Name"
                                     RenderTransformOrigin="-9.3,0.5" Width="200" FontSize="16" />
                    </StackPanel>
                </DataTemplate>
                <local:FilterTemplateSelector x:Key="FilterTemplateSelector" 
                        StringTemplate="{StaticResource TStringTemplate}"
                        IntegerTemplate="{StaticResource TIntegerTemplate}"
                        DropDownTemplate="{StaticResource TDropDownTemplate}"
                        />
            </UserControl.Resources>
            <Grid>
                <ItemsControl ItemsSource="{Binding FilterDetails}" >
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <UniformGrid Columns="3" />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                                <ContentControl 
                                    Content="{Binding}" 
                                    HorizontalAlignment="Left" 
                                    ContentTemplateSelector="{StaticResource FilterTemplateSelector}" />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Grid>
        </UserControl>
    

    最后定义模板选择器

    public class FilterTemplateSelector : DataTemplateSelector
        {
            public DataTemplate StringTemplate { get; set; }
            public DataTemplate IntegerTemplate { get; set; }
            public DataTemplate DropDownTemplate { get; set; }
    
            public override DataTemplate SelectTemplate(object item,
                         DependencyObject container)
            {
                var filter = (item as FilterAttribute);
                if (filter == null) return StringTemplate;
    
                if (!filter.IsDropDown)
                {
                    switch (filter.FilterDataType.Name.ToLower())
                    {
                        case "int32":
                        case "int64":
                            return IntegerTemplate;
                        case "string":
                            return StringTemplate;
                    }
                }
                else
                {
                    // display drop down
                    switch (filter.DropDownList)
                    {
                        case "Country":
                            filter.ObjectDropDownList = GetDropDown.GetCountries().ToList<object>();
                            break;
                    }
                    return DropDownTemplate;
                }
    
                return StringTemplate;
            }
        }