2

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?

3
  • A parent-child relation, with a "multi-select" parent; makes little sense; particularly to a "user". Side by side views; even if they're the same (data); sure. Multiple "view ports". Commented Aug 7 at 15:28
  • Can this be a bug on the side of syncfusion? If say, you put a MAUI collection view instead of syncfusion data grid, will you have the same problem? Commented Aug 11 at 5:13
  • Sounds like you’ll need to manually force a recalculation of the right-side grid. It’s not recognizing that the presence/absence of scrollbar might change. Perhaps if the FIRST displayed list required scrollbar, it would correctly enable/disable as needed. An implementation bug when it starts false. Commented Sep 2 at 8:48

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.