Reputation: 236
I have a WPF MVVM application with a DataGrid and a ComboBox that are binded to the same List of entities in a ViewModel class. I want to filter the DataGrid entries through the ComboBox selection, what is the proper way to do this? Since I'm working with MVVM, I want to achieve this with data bindings, and avoid useless code behind.
My XAML code is like the following
<DataGrid ItemsSource="{Binding Posts}" AutoGenerateColumns="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Id}" />
<DataGridTextColumn Header="Title" Binding="{Binding Title}" />
<DataGridTextColumn Header="BlogUrl" Binding="{Binding Blog.Url}" />
</DataGrid.Columns>
</DataGrid>
<ComboBox ItemsSource="{Binding Posts}"
DisplayMemberPath="Blog.Url" />
ViewModel
public class MainWindowViewModel
{
private SqliteDbContext context;
public List<Post> Posts { get; set; }
public MainWindowViewModel()
{
context = new SqliteDbContext();
Posts = context.Posts.Include(p => p.Blog).ToList();
}
}
In addition, with this code my ComboBox shows duplicates of Urls, how can I distinct these values?
Thanks.
Upvotes: 1
Views: 3235
Reputation: 1066
This should do the trick.
ViewModel:
public class MainWindowViewModel
{
private SqliteDbContext context;
public ObservableCollection<Post> Posts { get; set; }
private string _selectedUrl;
public ICollectionView PostsView { get; set; }
public MainWindowViewModel()
{
context = new SqliteDbContext();
Posts = new ObservableCollection<Post>(context.Posts.Include(p => p.Blog));
PostsView = new CollectionViewSource { Source = Posts }.View;
PostsView.Filter = post => SelectedUrl == null || SelectedUrl == ((Post)post).Blog.Url;
}
public string SelectedUrl
{
get
{
return _selectedUrl;
}
set
{
_selectedUrl = value;
PostsView.Refresh();
}
}
}
XAML:
<DataGrid ItemsSource="{Binding PostsView}" AutoGenerateColumns="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Id}" />
<DataGridTextColumn Header="Title" Binding="{Binding Title}" />
<DataGridTextColumn Header="BlogUrl" Binding="{Binding Blog.Url}" />
</DataGrid.Columns>
</DataGrid>
<ComboBox ItemsSource="{Binding Posts}"
DisplayMemberPath="Blog.Url"
SelectedValuePath="Blog.Url"
SelectedValue="{Binding SelectedUrl}"/>
Upvotes: 2
Reputation: 169150
You could bind the ComboBox
to a collection of the unique urls that you create in the view model.
You could then filter the DataGrid
by binding the SelectedItem
property of the ComboBox
to a source property of the view model that filters the Posts
source Collection.
Please refer to the following code sample.
View Model:
public class MainWindowViewModel : INotifyPropertyChanged
{
private readonly SqliteDbContext context;
private readonly List<Post> _allPosts;
public MainWindowViewModel()
{
context = new SqliteDbContext();
_allPosts = context.Posts.Include(p => p.Blog).ToList();
_posts = _allPosts;
Urls = _allPosts.Where(p => p.Blog != null && !string.IsNullOrEmpty(p.Blog.Url)).Select(p => p.Blog.Url).ToList();
}
private List<Post> _posts;
public List<Post> Posts
{
get { return _posts; }
set { _posts = value; NotifyPropertyChanged(); }
}
public List<string> Urls { get; set; }
private string _url;
public string Url
{
get { return _url; }
set
{
_url = value; NotifyPropertyChanged();
Posts = _allPosts.Where(p => p.Blog != null && p.Blog.Url == _url).ToList();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
View:
<DataGrid ItemsSource="{Binding Posts}" AutoGenerateColumns="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Id}" />
<DataGridTextColumn Header="Title" Binding="{Binding Title}" />
<DataGridTextColumn Header="BlogUrl" Binding="{Binding Blog.Url}" />
</DataGrid.Columns>
</DataGrid>
<ComboBox ItemsSource="{Binding Urls}" SelectedItem="{Binding Url}" />
Upvotes: 1