将多个列表框绑定到wpf中的单个搜索

时间:2016-01-09 04:51:20

标签: c# wpf

我在这里陷入了困境。所以我有多个堆叠在一起的扩展器。在每个扩展器内部是一个ListBox,数据绑定,其中每个listitem显示一个对象的名称。

我已经将搜索范围限制为根据名称过滤列表项。但是,由于我有两个可观察对象,过滤后的项目并且未经过滤,因此在有人搜索之前,UI似乎不会被填充。什么是解决这个问题的最佳方法。每次创建新Person时,我发现将项添加到两个列表都是多余的。使用mvvm方法。

这两个系列名为 PeopleFiltered 。当我创建人员时,我将它们添加到名为People的列表中。应用搜索时,它会填充PeopleFiltered列表,这是UI绑定的列表。如何将此列表保持为模仿人物。

在一天结束时,除非正在应用搜索,否则PeopleFiltered系列应该模仿人物。

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        WindowStartupLocation="CenterScreen"
        Title="MainWindow" Height="400" Width="200">

    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>

    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Grid Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <Label Content="Search:"/>
            <TextBox Grid.Column="1" Background="Gold" Text="{Binding SearchString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        </Grid>


        <StackPanel Grid.Row="1">
            <Expander Header="People" IsExpanded="{Binding IsExpanded, Mode=OneWay}">
                <ListView ItemsSource="{Binding PeopleFiltered}">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <WrapPanel>
                                <Ellipse Width="8" Height="8" Fill="Green" Margin="0,0,5,0"/>
                                <TextBlock Text="{Binding Name}"/>
                            </WrapPanel>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </Expander>
        </StackPanel>
    </Grid>
</Window>

MainWindowViewModel.cs

using System.Collections.ObjectModel;
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Linq;
using System.Collections.Generic;

namespace WpfApplication1
{
    public class MainWindowViewModel : NotifyBase
    {
        // search text
        private string searchString;
        public string SearchString
        {
            get { return searchString; }
            set
            {
                this.searchString = value;
                NotifyPropertyChanged("SearchString");
                ApplySearchFilter();
            }
        }

        private void ApplySearchFilter()
        {
            if (string.IsNullOrWhiteSpace(SearchString))
            {
                IsExpanded = false;
                PeopleFiltered.Clear();
                foreach (DisplayItem displayItem in People)
                {
                    PeopleFiltered.Add(displayItem);
                }
            }
            else
            {
                // open expanders and apply search
                IsExpanded = true;
                PeopleFiltered.Clear();
                foreach (DisplayItem displayItem in People)
                {
                    if (displayItem.Name.ToLowerInvariant().Contains(SearchString.ToLowerInvariant()))
                    {
                        PeopleFiltered.Add(displayItem);
                    }
                }
            }
        }

        // used to to open and close expanders
        private bool isExpanded;
        public bool IsExpanded
        {
            get { return this.isExpanded; }
            set
            {
                this.isExpanded = value;
                NotifyPropertyChanged("IsExpanded");
            }
        }

        // data collections for each expander
        private ObservableCollection<DisplayItem> people;
        public ObservableCollection<DisplayItem> People
        {
            get { return people ?? (people = new ObservableCollection<DisplayItem>()); }
            set
            {
                people = value;
                NotifyPropertyChanged("People");
            }
        }

        private ObservableCollection<DisplayItem> peopleFiltered;
        public ObservableCollection<DisplayItem> PeopleFiltered
        {
            get { return peopleFiltered ?? (peopleFiltered = new ObservableCollection<DisplayItem>()); }
            set
            {
                peopleFiltered = value;
                NotifyPropertyChanged("PeopleFiltered");
            }
        }

        // init
        public MainWindowViewModel()
        {
            // People
            People.Add(new DisplayItem() { Name="John" });
            People.Add(new DisplayItem() { Name="Veta"});
            People.Add(new DisplayItem() { Name="Sammy"});
            People.Add(new DisplayItem() { Name = "Sarah" });
            People.Add(new DisplayItem() { Name = "Leslie" });
            People.Add(new DisplayItem() { Name = "Mike" });
            People.Add(new DisplayItem() { Name = "Sherry" });
            People.Add(new DisplayItem() { Name = "Brittany" });
            People.Add(new DisplayItem() { Name = "Kevin" });
        }
    }

    // class used to display all items
    public class DisplayItem
    {
        public string Name { get; set; }
    }

    //observable object class
    public class NotifyBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

1 个答案:

答案 0 :(得分:2)

这是一个不错的方法。

在您的视图模型中创建一个Query属性,该属性将绑定到您的过滤器TextBox

private string _Query;

public string Query
{
    get { return _Query; }
    set
    {
        _Query = value;

        Filter();

        //Notify property changed.
    }
}

这里需要注意的是Filter()方法。每次属性更改时都会调用此属性,稍后我会再回到此处。首先,请确保TextBox绑定为TwoWay,它看起来像这样:

<TextBox Text="{Binding Query}" ... />

在您的视图模型中,您需要为每个ListBox添加一个集合。

private List<object> _Collection1; //The original collection
private List<object> _FilteredCollection1; //The filtered collection

public List<object> FilteredCollection1
{
    get { return _FilteredCollection1; }
    set
    {
        _FilteredCollection1 = value;

        //Notify property changed.
    }
}

//Some more collections
...

这里需要注意的是,原始未过滤集合有一个变量。这很重要,因为我们希望将此列表过滤为集合,否则我们只会反复进行过滤,最终在集合中没有任何内容。

您需要将ItemsSource中的ListBox属性绑定到集合中。

<ListBox ItemsSource="{Binding FilteredCollection1}" ... />

现在,您的Filter方法只需将_Collection1变量过滤到FilteredCollection1属性中即可。

private void Filter()
{
    //Perform the filter here for all collections.
    FilteredCollection1 = _Collection1.Where(x => x.Something == Query);

    //Do the same for all other collections...
}

注意:上面的Linq只是一个例子,我希望你的会稍微复杂一些,但是你明白了。

因此。每当Query更新时,Filter集合都会触发,更新FilteredCollection属性,这些属性又会调用属性更改并更新视图。

替代方法

这是另一种方式。

您可以将过滤器代码放在Filter属性的get块中,而不是使用FilteredCollection方法,如下所示:

public List<object> FilteredCollection1
{
    get 
    {
        return _Collection1.Where(...);
    }
}

然后,在您的Query媒体资源中,只需为集合调用INotifyPropertyChanged

private string _Query;

public string Query
{
    get { return _Query; }
    set
    {
        _Query = value;

        //Notify property changed.

        OnPropertyChanged("FilteredCollection1");
    }
}

这将强制视图刷新FilteredCollection1属性。