Reputation: 930
I have a dropdown with three filter options: All, Active, and Inactive. Changing the selected value raises an event that adjusts the observable collection property ProjectDbUsers
in the ViewModel, which a Datagrid showing application users is bound to.
The code successfully changes the ProjectDbUsers
property when I debug, but the Datagrid is not refreshing/updating to match the changed object collection.
Note: I'm using the CommunityToolkit.WinUI.Controls.DataGrid
package in the main UI project and the CommunityToolkit.Mvvm
package in the business layer project that contains the ViewModel.
The code:
In the Manage Users page XAML, showing the Combobox and Datagrid:
<ComboBox x:Name="BasicUserFilter" Grid.Row="0" Grid.Column="1"
Height="30" Width="Auto" Margin="20,10,10,10"
ItemsSource="{x:Bind ViewModel.Filters, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.SelectedFilter, Mode=TwoWay}"
SelectionChanged="BasicUserFilter_SelectionChanged"/>
<controls:DataGrid x:Name="dataGrid1" Grid.Row="2"
Height="600" Margin="12"
AutoGenerateColumns="False"
ItemsSource="{x:Bind ViewModel.ProjectDbUsers, Mode=OneWay}">
<controls:DataGrid.Columns>
<controls:DataGridTextColumn
Header="Name"
Width="250"
Binding="{Binding UserAddisplayName}"
FontSize="14" />
...
</controls:DataGrid.Columns>
</controls:DataGrid>
The code behind where the BasicUserFilter_SelectionChanged
event fires when changing the filter Combobox value.
private void BasicUserFilter_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ViewModel.UpdateGridBasedOnFilter();
}
Excerpts from the ViewModel class, showing the bound property definitions and the method called when the filter dropdown selection has changed:
[ObservableProperty]
private string _selectedFilter;
public ObservableCollection<string> Filters { get; } = new ObservableCollection<string>(new List<string>() { "All", "Active", "Inactive" });
public ObservableCollection<UserAd> ProjectDbUsers { get; private set; }
public void UpdateGridBasedOnFilter()
{
using (UnitOfWorkDbGlobal unitOfWorkDbGlobal = new(new MyDBContext()))
{
List<UserAd> users = new();
switch (SelectedFilter)
{
case "All":
users = unitOfWorkDbGlobal.UserAds.GetAllUsers().ToList();
break;
case "Active":
users = unitOfWorkDbGlobal.UserAds.GetAllActiveUsers().ToList();
break;
case "Inactive":
users = unitOfWorkDbGlobal.UserAds.GetAllInactiveUsers().ToList();
break;
}
ProjectDbUsers = new ObservableCollection<UserAd>(users);
}
}
What might be missing in order to allow the Datagrid to update when the ProjectDbUsers
changes?
Upvotes: 0
Views: 969
Reputation: 13716
Since you are reinstantiating the projectDbUsers property itself, you need to make it ObservableProperty.
Not directly related to your question, but instead of calling BasicUserFilter_SelectionChanged()
in code-behind, you can use partial
methods generated by the CommunityToolkit.Mvvm.
See this sample code.
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace DataGridTest;
public enum Filters
{
All,
Active,
Inactive,
}
public class UserAd
{
public string Name { get; set; } = string.Empty;
}
public partial class MainPageViewModel : ObservableObject
{
private List<UserAd> userAdSource = new()
{
new UserAd() { Name = "User A - Active" },
new UserAd() { Name = "User B - Inactive" },
new UserAd() { Name = "User C - Active" },
new UserAd() { Name = "User D - Inactive" },
new UserAd() { Name = "User E - Active" },
};
// The CommunityToolkit.Mvvm auto-generates
// partial void OnSelectedFilterChanged(Filters value).
[ObservableProperty]
private Filters selectedFilter = Filters.All;
// ObservableCollections notifies the UI when you add/remove elements.
// Since you are reinstantiating the projectDbUsers property itself,
// you need to make it ObservableProperty.
//public ObservableCollection<UserAd> ProjectDbUsers { get; set; }
[ObservableProperty]
private ObservableCollection<UserAd>? projectDbUsers;
public MainPageViewModel()
{
IEnumerable<UserAd> filteredUserAds = GetFilteredUserAds(SelectedFilter);
ProjectDbUsers = new ObservableCollection<UserAd>(filteredUserAds);
}
public List<Filters> FilterList { get; } = new(Enum.GetValues<Filters>());
// This "partial method" is auto-generated by the CommunityTookit.Mvvm.
async partial void OnSelectedFilterChanged(Filters value)
{
IEnumerable<UserAd> filteredUserAds = await GetFilteredUserAds(value);
ProjectDbUsers = new ObservableCollection<UserAd>(filteredUserAds);
}
private Task<IEnumerable<UserAd>> GetFilteredUserAds(Filters filter)
{
return await Task.Run(() =>
{
return filter is Filters.All
? this.userAdSource
: this.userAdSource.Where(x => x.Name.Contains(filter.ToString()));
});
}
}
MainPage.xaml
<Page
x:Class="DataGridTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.UI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:DataGridTest"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ComboBox
Grid.Row="0"
ItemsSource="{x:Bind ViewModel.FilterList}"
SelectedItem="{x:Bind ViewModel.SelectedFilter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<controls:DataGrid
Grid.Row="1"
AutoGenerateColumns="False"
ItemsSource="{x:Bind ViewModel.ProjectDbUsers, Mode=OneWay}">
<controls:DataGrid.Columns>
<controls:DataGridTextColumn
Width="250"
Binding="{Binding Name}"
FontSize="14"
Header="Name" />
</controls:DataGrid.Columns>
</controls:DataGrid>
</Grid>
</Page>
Upvotes: 1