简体   繁体   English

如何使用DataTemplate实现对WPF ListBox的搜索?

[英]How can I implement search for a WPF ListBox with DataTemplate?

I'd like to implement search for a WPF prototype I'm building in which I'm using a ListBox with a DataTemplate. 我想实现对正在构建的WPF原型的搜索,在该原型中,我将使用带有DataTemplate的ListBox。 I'm trying to devise a way to search the text value of a few TextBlock controls on each item and then set the Visibility to Collapsed on any items that don't contain the search text. 我正在尝试设计一种方法来搜索每个项目上几个TextBlock控件的文本值,然后在不包含搜索文本的任何项目上将“可见性”设置为“折叠”。

I figure that one way I could do this would be to include a property on the Model to which the ListBox Items are bound, bind the Item's Visibility property to that Model property via a ValueConverter, and set that property during search, but seems kind of kludgy to me. 我认为可以做到这一点的一种方法是在Model上包含一个将ListBox Items绑定到的属性,通过ValueConverter将Item的Visibility属性绑定到该Model属性,并在搜索过程中设置该属性,但似乎有点对我来说很糊涂。 Any and all thoughts are appreciated! 任何和所有的想法表示赞赏! Thank you! 谢谢!

The simplest answer would be to use a CollectionViewSource(CVS) to contain your collection, and have the listbox bound to the CVS. 最简单的答案是使用CollectionViewSource(CVS)来包含您的集合,并将列表框绑定到CVS。 CVS can perform filters, sorts, and groups without affecting the collection. CVS可以执行过滤器,排序和分组,而不会影响集合。

CODEBEHIND Method CODEBEHIND方法

Essentially you will have an event handler which tells the CVS to perform a new filter as delegate. 本质上,您将拥有一个事件处理程序,该事件处理程序告诉CVS作为委托执行新的过滤器。 Mine generally look something like this: 我的通常看起来像这样:

Private Sub MyEventHandler()
    _ShipmentCollectionView.Filter = New Predicate(Of Object)(AddressOf FilterOut)
End Sub

Private Function FilterOut(ByVal item As Object) As Boolean
        Dim MyShipment As Shipment = CType(item, Shipment)
        If _FilterDelivered And MyShipment.TransitStatus = eTransitStatus.Delivered Then
            Return False
        End If
        If _FilterOverdue And MyShipment.TransitStatus = eTransitStatus.InTransit AndAlso MyShipment.ExpectedDate < Today Then
            Return False
        End If
        If _FilterUnshipped And MyShipment.TransitStatus = eTransitStatus.Unshipped Then
            Return False
        End If
        If SearchString Is Nothing Or SearchString = "" Then
            Return True
        Else

            Return MyShipment.Contains(SearchString)
        End If
    End Function

What this does is pass the items through that FilterOut method, and return whether or not they fit the filter. 这样做是通过该FilterOut方法传递项目,并返回它们是否适合过滤器。 If they do, the CollectionView (or which the CVS is a part) tells the UI which items to display. 如果这样做,CollectionView(或CVS的一部分)会告诉UI要显示哪些项目。

The MVVM Method MVVM方法

(My Favorite) (我的最爱)

This method differs in that all filter option controls are bound to properties in the ViewModel. 此方法的不同之处在于,所有过滤器选项控件都绑定到ViewModel中的属性。 My favorite thing to do with this is to put the _ShipmentCollectionView.Filter = New Predicate(Of Object)(AddressOf FilterOut) line in the SearchText property setter. 我最喜欢做的是将_ShipmentCollectionView.Filter = New Predicate(Of Object)(AddressOf FilterOut)行放在SearchText属性设置器中。 This way, the filter operation is run every time a user types a letter (Continuous Filtering), that is presuming they have the binding made correctly (ie Text="{Binding SearchString, UpdateSourceTrigger=PropertyChanged}") 这样,每次用户键入字母(假定他们已正确进行绑定)(即Text =“ {Binding SearchString,UpdateSourceTrigger = PropertyChanged}”)时,都会运行筛选器操作(连续筛选)

Personally I recommend MVVM pattern for all but the simplest projects. 我个人建议除了最简单的项目外,将MVVM模式用于所有项目。 It allows for a lot more neat stuff to be done with a lot less hassle. 它允许完成许多更整洁的工作,而麻烦却少得多。

Hope that helps. 希望能有所帮助。

Links! 链接!

I've found the easiest way is to apply Filter to the CollectionViewSource , rather than updating each item individually. 我发现最简单的方法是将Filter应用于CollectionViewSource ,而不是分别更新每个项目。

public ObservableCollection<MyObject> Entries { get; set; }
public CollectionViewSource View { get; set; }

private string _searchText;
public string SearchText
{
    get { return _searchText; }
    set
    {
        if (_searchText == value)
            return;
        _searchText = value;
        View.Filter -= ApplySearch;
        if (!string.IsNullOrWhiteSpace(_searchText))
            View.Filter += ApplySearch;
    }
}

public MyClass()
{
    Entries = new ObservableCollection<MyObject>();
    View = new CollectionViewSource { Source = Entries };
}

private void ApplySearch(object sender, FilterEventArgs e)
{
        var item = e.Item as MyObject;
        if (item == null)
            return;
        if (item.FirstProperty.IndexOf(SearchText) < 0 && item.SecondProperty.IndexOf(SearchText) < 0)
            e.Accepted = false;
}

Note: In this case there's no need for a search button, but it may be a good idea to use a DispatcherTimer to set a delay before setting the filters. 注意:在这种情况下,不需要搜索按钮,但是在设置过滤器之前使用DispatcherTimer设置延迟可能是个好主意。

Use CollectionView: 使用CollectionView:

ICollectionView collectionView = CollectionViewSource.GetDefaultView(_source);
collectionView.Filter = new Predicate<object>(YourFilterFunction);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM