I'm creating a .NET MAUI project with two data grids. The first data grid will show category selections and is multi-select. The second one shows a list of items that are displayed based on what category is selected.
Now the second data grid has a height set to 5 rows. I do this by adding this command in the OnAppearing method in the code behind:
passwordGrid.HeightRequest = passwordGrid.HeaderRowHeight + passwordGrid.RowHeight * 5;
Now when I initially select a category from the first data grid that contains 5 item, the items are displayed in grid 2 no problem.
However, my problem occurs when I select a second category which now is suppose display the first 5 selected items plus a vertical scroll bar which can be used to scroll through the list.
What I get when I select the second category is a disabled vertical scroll bar. The scroll bar will only be enabled if I manually horizontally resize the content page until the right border of the second grid disappears, and then I resize the content page so the grid border reappears.
You can get a better look at what I've described by watching this video.
Here is the XAML markup of the content page:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:syncfusion="clr-namespace:Syncfusion.Maui.DataGrid;assembly=Syncfusion.Maui.DataGrid"
xmlns:local="clr-namespace:DataGrid"
x:Class="DataGrid.MainPage">
<ContentPage.Content>
<Grid RowDefinitions="Auto,*"
Padding="10">
<Button Grid.Row="0"
Text="Select Database"
HorizontalOptions="Center"
Margin="0,10,0,20"
BackgroundColor="#512BD4"
Clicked="OnSelectDatabaseClicked"/>
<Grid Grid.Row="1"
RowDefinitions="Auto,*"
ColumnDefinitions="*,*"
RowSpacing="5"
ColumnSpacing="20">
<Label Grid.Row="0" Grid.Column="0"
Text="Password Groups"
FontAttributes="Bold"
FontSize="16"
Margin="0,0,0,2"/>
<Label Grid.Row="0" Grid.Column="1"
Text="Passwords"
FontAttributes="Bold"
FontSize="16"
Margin="0,0,0,2"/>
<syncfusion:SfDataGrid Grid.Row="1" Grid.Column="0"
HorizontalOptions="Start"
WidthRequest="100"
x:Name="groupGrid"
SelectionMode="Multiple"
NavigationMode="Row"
SelectionChanged="GroupGrid_SelectionChanged"
AutoGenerateColumnsMode="None"
HeaderRowHeight="45"
HeaderGridLinesVisibility="Both"
GridLinesVisibility="Both"
ItemsSource="{Binding Groups}">
<syncfusion:SfDataGrid.Columns>
<syncfusion:DataGridTextColumn HeaderText="Group Name"
MappingName="Name"
Width="150"/>
</syncfusion:SfDataGrid.Columns>
</syncfusion:SfDataGrid>
<syncfusion:SfDataGrid Grid.Row="1" Grid.Column="1"
HorizontalOptions="Start"
VerticalOptions="Start"
x:Name="passwordGrid"
AutoGenerateColumnsMode="None"
VerticalScrollBarVisibility="Always"
SelectionMode="Multiple"
NavigationMode="Row"
SortingMode="Single"
BackgroundColor="White"
HeaderRowHeight="30"
Margin="20,30,20,0"
HeaderGridLinesVisibility="Both"
GridLinesVisibility="Both"
MinimumHeightRequest="0"
IsVisible="{Binding PwVisible}"
ItemsSource="{Binding FilteredPasswords}">
<syncfusion:SfDataGrid.Columns>
<syncfusion:DataGridTextColumn HeaderText="Title"
MappingName="Name"
Width="100"/>
<syncfusion:DataGridTextColumn HeaderText="Username"
MappingName="BillStatus"
Width="150"/>
<syncfusion:DataGridTextColumn HeaderText="Order ID"
MappingName="OrderID"
Width="100"/>
</syncfusion:SfDataGrid.Columns>
</syncfusion:SfDataGrid>
</Grid>
</Grid>
</ContentPage.Content>
</ContentPage>
And here is the code behind:
using CommunityToolkit.Maui.Views;
using DataGrid.Models;
using DataGrid.Services;
using Syncfusion.Maui.DataGrid;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace DataGrid
{
public partial class MainPage : ContentPage
{
private PasswordViewModel viewModel;
public MainPage()
{
InitializeComponent();
viewModel = new PasswordViewModel();
this.BindingContext = viewModel;
}
protected override void OnAppearing()
{
base.OnAppearing();
// Only set the height request for the grid
passwordGrid.HeightRequest = passwordGrid.HeaderRowHeight + passwordGrid.RowHeight * 5;
}
private async void OnSelectDatabaseClicked(object sender, EventArgs e)
{
await ShowDatabaseSelectionPopup();
}
private async Task ShowDatabaseSelectionPopup()
{
var popup = new DatabaseSelectionPopup();
var result = await this.ShowPopupAsync(popup);
if (result is bool selectDatabase && selectDatabase)
{
viewModel.LoadDatabaseData();
}
}
private void GroupGrid_SelectionChanged(object sender, DataGridSelectionChangedEventArgs e)
{
var grid = sender as SfDataGrid;
if (grid == null)
return;
var selectedRows = grid.SelectedRows;
foreach (var item in viewModel.Groups)
{
item.IsSelected = selectedRows.Contains(item);
}
// refresh the passwords grid
viewModel.UpdatePasswordsBasedOnSelectedGroups();
}
}
public class Password
{
public int Id { get; set; }
public string Title { get; set; }
public string Username { get; set; }
public string Value { get; set; }
public int GroupId { get; set; }
public Password(int id, string title, string username, string value, int groupId)
{
Id = id;
Title = title;
Username = username;
Value = value;
GroupId = groupId;
}
}
public class PasswordViewModel : INotifyPropertyChanged
{
private ObservableCollection<PasswordGroup> _groups;
private ObservableCollection<OrderItem> _allPasswords;
private ObservableCollection<OrderItem> _filteredPasswords;
private bool _pwVisible;
private DatabaseService _databaseService;
public event PropertyChangedEventHandler? PropertyChanged;
public ObservableCollection<PasswordGroup> Groups
{
get { return _groups; }
set
{
_groups = value;
OnPropertyChanged(nameof(Groups));
}
}
public ObservableCollection<OrderItem> AllPasswords
{
get { return _allPasswords; }
set
{
_allPasswords = value;
OnPropertyChanged(nameof(AllPasswords));
}
}
public ObservableCollection<OrderItem> FilteredPasswords
{
get { return _filteredPasswords; }
set
{
_filteredPasswords = value;
OnPropertyChanged(nameof(FilteredPasswords));
}
}
public bool PwVisible
{
get { return _pwVisible; }
set
{
if (_pwVisible != value)
{
_pwVisible = value;
OnPropertyChanged(nameof(PwVisible));
}
}
}
public PasswordViewModel()
{
_databaseService = new DatabaseService();
_groups = _databaseService.Groups;
_allPasswords = _databaseService.OrderItemsDataSource;
_filteredPasswords = new ObservableCollection<OrderItem>();
}
public void LoadDatabaseData()
{
// This simulates loading data from a database selected via popup
_databaseService.InitializeDatabase();
// Make sure our collections reference the updated data
Groups = _databaseService.Groups;
AllPasswords = _databaseService.OrderItemsDataSource;
}
public void UpdatePasswordsBasedOnSelectedGroups()
{
FilteredPasswords.Clear();
var selectedGroupIds = Groups.Where(g => g.IsSelected).Select(g => g.Id).ToList();
foreach (var password in AllPasswords)
{
if (selectedGroupIds.Contains(password.GroupId))
{
FilteredPasswords.Add(password);
}
}
if (FilteredPasswords.Count > 0)
{
// Make PwVisible true only when there are passwords to show, false otherwise
PwVisible = true;
}
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
My question: why do I have to manually resize the content page to enable the scroll bar?