I've got a dilemma here. So I have mutiple expanders stacked on top of one another. Inside each expander is a ListBox, data bound, where each listitem displays a name of an object.
I've bound the search to filter the list items based on their name. However since I have two observable objects, the filtered items and unfiltered, the UI doesn't appear to get populated until someone searches. Whats the best way to fix this. I find it redundant to add items to both lists each time a new Person gets created. Using an mvvm approach.
The two collections are called People and PeopleFiltered. When i create people I add them to the list called People. When the search is applied it populates the PeopleFiltered list, which is the list the UI is bound to. How can I maintain this list be init to mimic People.
At the end of the day the PeopleFiltered collection should mimic People unless a search is being applied.
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));
}
}
}
}