我在这里陷入了困境。所以我有多个堆叠在一起的扩展器。在每个扩展器内部是一个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));
}
}
}
}
答案 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
属性。