过滤表格以搜索外键数据

时间:2014-03-10 12:00:59

标签: wpf listview binding filter

这个问题的标题可能听起来令人困惑,所以我会尝试以最好的方式解释自己。

我正在创建一个包含许多foreign key个约束的表的应用程序。例如,在我的情况下,我有一个学生。每个学生表与父母详细信息表和医疗详细信息表都有foreign key关系。

为了使我的应用程序易于使用,我实现了一系列不同的filters来帮助用户搜索大量数据。

我有一个listview来显示学生记录,并且与父母和医疗详细信息相同。但是,我想要做的是根据父母详细信息中的一组标准搜索学生记录。例如,搜索父母姓名。例如;如果学生有一个名为Bob的父母,listviewfilter的父母称为Bob的学生。

这是我尝试过的;

   //Constructor;
   StudentList = new ObservableCollection<StudentViewModel>(GetStudents());

   CollectionViewSource.GetDefaultView(StudentList).Filter = new Predicate<object>(MainFilter);

   //Properties
   private string contactNameSearch;
   public string ContactNameSearch
   {
       get { return contactNameSearch; }
       set
       {
           contactNameSearch = value;
           CollectionViewSource.GetDefaultView(StudentList).Refresh();
           OnPropertyChanged("ContactNameSearch");
       }
   }

   private bool FilterContactNameSearch(object obj)
   {
       StudentContactViewModel item = obj as StudentContactViewModel;
       if (item == null) return false;
       if (String.IsNullOrWhiteSpace(ContactNameSearch)) return true;

       if (ContactNameSearch.Trim().Length == 0) return true;

       if (item.Name1.ToLower().Contains(ContactNameSearch.ToLower())) return true;
       return false;
   }

   public bool MainFilter(object o)
   {
       return FilterContactNameSearch(o); // &... and more filters
   }

   //Xaml
   <TextBox Height="23" Name="txtContactName" Width="100" 
                    Text="{Binding ContactNameSearch, UpdateSourceTrigger=PropertyChanged}"/>

我上面的代码片段是我为其他filter实现的,所有这些都可以正常工作。但是,当我bind property到我的应用程序中的textbox时,突然填充listview的数据消失了。 This link here shows the before and after

如果我在 ContactNameSearch breakpoint周围加property,我也会看到“No Window Source”对话框。

我已经汇总了一个可以找到的小样本by click here

因此,我的问题是;我是否正确地实现了这一点,如果没有,是否有另一种方法可以做到这一点?

1 个答案:

答案 0 :(得分:1)

在我的应用程序中为用户提供过滤功能时,我倾向于使用两个集合。一个人拥有整个未经过滤的收藏品,一旦填满,它就会保持不变。另一种是仅包含第一个集合中与给定过滤条件匹配的项目。第二个集合是绑定到UI中ItemsControl.ItemsSource属性的数据。

public ObservableCollection<YourDataType> DataTypes
{
    get { return dataTypes; }
    set
    {
        if (dataTypes != value) 
        {
            dataTypes = value; 
            NotifyPropertyChanged("DataTypes");
            FilterDataTypes();
        }
    }
}

public ObservableCollection<YourDataType> FilteredDataTypes // Data bind this one
{
    get { return filteredDataTypes ?? (filteredDataTypes = DataTypes); }
    private set { filteredDataTypes = value; NotifyPropertyChanged("FilteredDataTypes"); }
}

现在,您的FilterDataTypes方法基本上需要通过您认为合适的方式过滤DataTypes集合并填充FilteredDataTypes集合。此示例使用string输入,该输入是绑定到UI中的过滤器框的数据,实际过滤条件采用CheckFields方法。

private void FilterDataTypes()
{
    filteredDataTypes = new ObservableCollection<YourDataType>();
    string filterText = FilterText.Trim().ToLower();
    if (filterText == string.Empty)
    {
        foreach (YourDataType dataType in DataTypes)
        {
            FilteredDataTypes.Add(dataType);
        }
    }
    else
    {
        foreach (YourDataType dataType in DataTypes.Where(m => CheckFields(m)))
        {
            FilteredDataTypes.Add(dataType);
        }
    }
    NotifyPropertyChanged("FilteredDataTypes");
}

private bool CheckFields(YourDataType dataType)
{
    string filterText = FilterText.Trim().ToLower();
    return filterText == string.Empty ? true : 
        dataType.Parent.Name.ToLower().Contains(filterText);
}

public string FilterText
{
    get { return filterText; }
    set
    {
        if (filterText != value)
        {
            filterText = value;
            NotifyPropertyChanged("FilterText");
            FilterDataTypes(); // <-- Filters collection when value is changed
        }
    }
}

现在这个简单的示例只有一个过滤器输入,但您可以使用其他方法(如CheckFields方法)添加任意数量,但基于其他数据绑定属性的值:

private void FilterDataTypes()
{
    filteredDataTypes = new ObservableCollection<YourDataType>();
    string filterText = FilterText.Trim().ToLower();
    if (filterText == string.Empty)
    {
        foreach (YourDataType dataType in DataTypes)
        {
            FilteredDataTypes.Add(dataType);
        }
    }
    else
    {
        foreach (YourDataType dataType in 
            DataTypes.Where(m => CheckFields(m) | CheckOptions(m)))
        {
            FilteredDataTypes.Add(dataType);
        }
    }
    NotifyPropertyChanged("FilteredDataTypes", "DataTypesCount");
}

public YourDataType SelectedItem // <-- Data bind to ItemsControl.SelectedItem
{
    get { return selectedItem; }
    set
    {
        if (selectedItem != value)
        {
            selectedItem = value;
            NotifyPropertyChanged("SelectedItem");
            FilterDataTypes();
        }
    }
}

private bool CheckOptions(YourDataType dataType) // <-- And use to filter collection
{
    string filterText = SelectedItem.Name.Trim().ToLower();
    return filterText == string.Empty ? true : 
        dataType.Doctor.Name.ToLower().Contains(filterText);
}