How predicate filters works with ListCollectionView? In my case i have ListCollectionView<Users> FilteredUserList
. To filter single value i am using
private void AddFilterAndRefresh(string name, Predicate<User> predicate)
{
//Adds filter to filter list
Filters.Add(name, predicate);
//Filters doesn't fire event automatically
OnPropertyChanged("Filters");
//Refresh list to by correctly displayed
FilteredUserList.Refresh();
}
Usage example AddFilterAndRefresh(key, user => user.Name.Contains(u.Name))
Now things from here is not understandable for me. When i use function above, in datagridview is shown only one row. So it means that before filter all values are "true" to show, but when i pass filter all values becomes false and one value true?
For stacking filters i use
private bool FilterEntries(object obj)
{
User c = (User)obj;
return Filters.Values.Aggregate(true, (prevValue, predicate) => prevValue && predicate(c));
}
But I want to make excel like filter, when user checks values what to show. It means that i have to filter multiple values. When i do foreach(User u in SelectedOptions) {AddFilterAndRefresh()}
Datagrid is empty - but it is obvious because after one filter datagrid shows one row. So how to filter multiple values?
Well. I did some modifications. It works but not correctly. Imagine I have user list:
Now if i exclude by Departament "unknown" (uncheck chekbox) it filters ok:
Now when i uncheck by Tabelis "20" it filter ok:
But when i out check on Tabelis "20" again it filters wrong:
Turns Departament values on.
Where i am doing a mistake? view:
<DataGrid x:Name="myGrd"
DataContext="{Binding ElementName=userPage, Path=DataContext}"
SelectionMode="Single"
SelectionUnit="Cell"
CurrentItem="{Binding SelectedUser, Mode=TwoWay}"
CurrentColumn="{Binding CurrentColumn, Mode=TwoWay}"
IsReadOnly="True"
CanUserResizeColumns="False"
Grid.Row="1"
ItemsSource="{Binding FilteredUserList}"
AutoGenerateColumns="True"
CanUserAddRows="False">
<DataGrid.Resources>
<!--Popup-->
<ContextMenu x:Key="ContextMenu">
<ContextMenu.Items>
<MenuItem Header="Filter by Selection" Command="{Binding IncludeCommand, Source={x:Reference vm}}"/>
<MenuItem Header="Filter exclude Selection" Command="{Binding ExcludeCommand, Source={x:Reference vm}}"/>
<MenuItem Header="Remove all Filters" Command="{Binding RemoveAllFiltersCommand, Source={x:Reference vm}}" Visibility="{Binding Filters.Count, Source={x:Reference vm}, Converter={Wpf:VisibilityConverter}}"/>
</ContextMenu.Items>
</ContextMenu>
<!--Custom Datagrid header View-->
<Style TargetType="DataGridColumnHeader" x:Name="FilterHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBox Margin="0,0,0,10" Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridColumnHeader}}, Path=Width}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" HorizontalAlignment="Center"/>
<ToggleButton Name="FilterButton"
Content="[-F-]"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.GenerateExcelFilterViewItemsCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGridColumnHeader}, Path=Content}"
>
</ToggleButton>
<Popup Name="MyPopup"
StaysOpen="False"
Placement="Right"
IsOpen="{Binding ElementName=FilterButton, Path=IsChecked}"
PlacementTarget="{Binding ElementName=FilterButton}"
>
<Border CornerRadius="0" BorderThickness="2" BorderBrush="{StaticResource AQBlueBrush}">
<StackPanel Width="Auto" Background="{StaticResource AQBackgroundLightBrush}" MinWidth="100">
<TextBlock Margin="2" Text="Filter" Foreground="{StaticResource AQVeryDarkBlueBrush}"/>
<Separator/>
<ListView Padding="5"
MaxHeight="150"
MinHeight="80"
Background="Transparent"
BorderThickness="0"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.FilterList}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.IsSelectedItemsCommand}"
CommandParameter="{Binding}"/>
<ContentPresenter Content="{Binding Value}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Margin="2,0,2,2"
Content="Submit"
Foreground="{StaticResource AQVeryDarkBlueBrush}"
Background="AliceBlue"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.AddMultipleFiltersAndRefreshCommand}"/>
</StackPanel>
</Border>
</Popup>
</StackPanel>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="ContextMenu" Value="{StaticResource ContextMenu}"/>
</Style>
</DataGrid.CellStyle>
</DataGrid>
ViewModel:
public class UsersViewModel: BaseViewModel
{
#region Private Properties
/// <summary>
/// User's information holding list that could be used
/// </summary>
private ObservableCollection<User> UserList = new ObservableCollection<User>();
/// <summary>
/// Selected Header in datagrid
/// </summary>
private string SelectedColumnHeader { get; set; }
/// <summary>
/// List of isSelected Values
/// </summary>
private ObservableCollection<FilterPredicate> SelectedFilters { get; set; } = new ObservableCollection<FilterPredicate>();
#endregion
#region public Properties
/// <summary>
/// Filter list used to excel like filter functionality
/// </summary>
public ObservableCollection<FilterPredicate> FilterList { get; set; }
/// <summary>
/// Filters every filter used is saved in this dictionary
/// </summary>
public List<Predicate<User>> Filters { get; set; } = new List<Predicate<User>>();
/// <summary>
/// Filtered user list
/// </summary>
public ListCollectionView FilteredUserList { get; set; }
/// <summary>
/// Current Selected Object
/// </summary>
public User SelectedUser { get; set; }
/// <summary>
/// Current Selected Column
/// </summary>
public DataGridColumn CurrentColumn { get; set; }
public bool PopupVisible { get; set; }
public UIElement FilterButton { get; set; }
#endregion
#region Commands
public ICommand ExcludeCommand { get; set; }
public ICommand IncludeCommand { get; set; }
public ICommand RemoveAllFiltersCommand { get; set; }
public ICommand RemoveFilterCommand { get; set; }
public ICommand GenerateExcelFilterViewItemsCommand { get; set; }
public ICommand IsSelectedItemsCommand { get; set; }
public ICommand AddMultipleFiltersAndRefreshCommand { get; set; }
#endregion
#region Constructor
/// <summary>
/// Default constructor
/// </summary>
public UsersViewModel()
{
//Populate UserList
GetUsers();
//Set filtered user list with values
FilterList = new ObservableCollection<FilterPredicate>();
FilteredUserList = new ListCollectionView(UserList);
ExcludeCommand = new RelayParamCommand((e) => ExcludeFilter(SelectedUser));
IncludeCommand = new RelayParamCommand((e) => IncludeFilter(SelectedUser));
RemoveAllFiltersCommand = new RelayCommand(() => RemoveAllFiltersAndRefresh());
GenerateExcelFilterViewItemsCommand = new RelayParamCommand((e) => GenerateExcelFilterItems(e));
IsSelectedItemsCommand = new RelayParamCommand((e) => IsSelectedItems(e));
//AddMultipleFiltersAndRefreshCommand = new RelayCommand(AddMultipleFilterss);
FilteredUserList.Filter = (e) => { return FilterEntries(e); };
}
#endregion
#region Filter Methods
/// <summary>
/// Filter Collection view values
/// </summary>
private bool FilterEntries(object obj)
{
User c = (User)obj;
bool isIn = true;
if (Filters.Count == 0)
{
//return Filters.TrueForAll(x => x(c));
return isIn;
}
else
{
return Filters.TrueForAll(x => x(c));
// return Filters.Aggregate(true, (prevValue, predicate) => prevValue && predicate(c));
}
}
/// <summary>
/// Exclude selected value
/// </summary>
/// <param name="obj"></param>
public void ExcludeFilter(object obj)
{
User u = (User)obj;
switch (CurrentColumn.DisplayIndex)
{
case 0:
AddFilterAndRefresh(user => !user.Tabelis.ToString().Contains(u.Tabelis.ToString()));
return;
case 1:
AddFilterAndRefresh(user => !user.Name.Contains(u.Name));
return;
case 2:
AddFilterAndRefresh(user => !user.Departament.Contains(u.Departament));
return;
}
}
/// <summary>
/// Include selected filter value
/// </summary>
/// <param name="obj">Object</param>
private void IncludeFilter(object obj)
{
User u = (User)obj;
switch (CurrentColumn.DisplayIndex)
{
case 0:
AddFilterAndRefresh(user => user.Tabelis.ToString().Contains(u.Tabelis.ToString()));
return;
case 1:
AddFilterAndRefresh(user => user.Name.Contains(u.Name));
return;
case 2:
AddFilterAndRefresh(user => user.Departament.Contains(u.Departament));
return;
}
}
/// <summary>
/// Add filter to Filter list
/// </summary>
/// <param name="name">Key</param>
/// <param name="predicate">Filter (predicate) object</param>
private void AddFilterAndRefresh(Predicate<User> predicate)
{
//Adds filter to filter list
Filters.Add(predicate);
//Filters doesn't fire event automatically
OnPropertyChanged("Filters");
//Refresh list to by correctly displayed
FilteredUserList.Refresh();
}
/// <summary>
/// Remove all filters from filter list
/// </summary>
private void RemoveAllFiltersAndRefresh()
{
Filters.Clear();
FilterList.Clear();
SelectedFilters.Clear();
FilteredUserList.Refresh();
OnPropertyChanged("Filters");
}
/// <summary>
/// Gets property to get filter items by this property value
/// </summary>
/// <param name="obj">Object property</param>
private void GenerateExcelFilterItems(object obj)
{
//set header name as string
SelectedColumnHeader = (string)obj;
//Clear Filter list
FilterList.Clear();
foreach(FilterPredicate i in SelectedFilters)
{
//insert right not selected value
//because after filter excecution that value dissapears
if (!FilterList.Contains(i) && i.ColumnName == SelectedColumnHeader) { FilterList.Add(i); }
}
//Fill filter list with new values depend on selectedColumnHeader property
FillFilterValues(SelectedColumnHeader);
}
/// <summary>
/// Remove or add "IsSelected" values to SelectedFilters List
/// </summary>
/// <param name="obj"></param>
private void IsSelectedItems(object obj)
{
var SelectedItem = (FilterPredicate)obj;
if (SelectedItem.IsSelected)
{
Filters.Add(AddMultipleFilters);
//Refresh list to by correctly displayed
FilteredUserList.Refresh();
}
else
{
FilterList[FilterList.IndexOf(SelectedItem)].IsSelected = false;
Filters.Add(AddMultipleFilters);
//RemoveMultipleFilters(SelectedItem);
if(!SelectedFilters.Contains(SelectedItem))
SelectedFilters.Add(SelectedItem);
//Refresh list to by correctly displayed
FilteredUserList.Refresh();
}
}
private bool AddMultipleFilters(object obj)
{
User u = (User)obj;
return FilterList.Any(x => x.IsSelected && x.Value.Contains(u.GetType().GetProperty(SelectedColumnHeader).GetValue(u).ToString()));
}
#endregion
#region helpers
/// <summary>
/// Fill filter items list with values from FilteredUsersList
/// </summary>
/// <param name="Property"></param>
private void FillFilterValues(string Property)
{
foreach (User u in FilteredUserList)
{
User i = u;
var item = new FilterPredicate(Property, u.GetType().GetProperty(Property).GetValue(i).ToString(), true);
if (!FilterList.Any(x => x.ColumnName == item.ColumnName && x.Value == item.Value))
FilterList.Add(item);
}
}
/// <summary>
/// Fill userList with data from database
/// </summary>
private void GetUsers()
{
UserList.Add(new User
{
Name = "Marius",
Departament = "some",
Tabelis = 5
});
UserList.Add(
new User
{
Name = "Darius",
Departament = "unknown",
Tabelis = 20
});
UserList.Add(
new User
{
Name = "Koste",
Departament = "unknown",
Tabelis = 20
});
UserList.Add(
new User
{
Name = "Gediminas",
Departament = "unknown",
Tabelis = 20
});
UserList.Add(
new User
{
Name = "Nerijus",
Departament = "Tech",
Tabelis = 20
});
}
#endregion
}
}
Filter
predicate should return true
for items which needs to be displayed, and false
for hidden items.
CollectionView needs only one predicate. To filter by selected names, keep them in a list and compare CollectionView item for possible match with any name , not with each name.
private bool FilterEntries(object obj)
{
User u = (User)obj;
// converting original condition to ANY instead of EACH
return Filters.Values.Aggregate(false, (prevValue, predicate) => prevValue || predicate(u));
// looks like Filters is a Dictionary, maybe simplify condition?
return Filters.ContainsKey(u.Name);
// or maybe search in a List without multiple predicates
return Filters.Keys.Any(name => name.Contains(u.Name));
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.