Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 23, 2025

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Description

Fixes header and footer toggle issues in CollectionView on Windows and Android platforms where:

  • Windows: Header/Footer content was not re-added after being set to null and then enabled again
  • Android: Header/Footer was not properly removed at runtime when set to null

Changes Made

Windows (StructuredItemsViewHandler.Windows.cs)

  1. Clear template when removing header/footer: Added explicit clearing of HeaderTemplate/FooterTemplate when header/footer is set to null. This ensures the native control properly releases the previous template reference.

  2. Prevent duplicate logical child additions: Added a check to only add the view as a logical child if it's not already a child of the element. This prevents issues when the same View instance is toggled multiple times.

// Before
case null:
    ListViewBase.Header = null;
    break;

// After
case null:
    ListViewBase.HeaderTemplate = null;
    ListViewBase.Header = null;
    break;

Android (StructuredItemsViewAdapter.cs)

Replaced the generic NotifyDataSetChanged() with specific item notifications for better RecyclerView management:

  • Header removed: NotifyItemRemoved(0) - Properly animates and removes the header item
  • Header added: NotifyItemInserted(0) - Properly animates and inserts the header item
  • Footer removed/added: Similar approach with correct position calculation
// Before
UpdateHasHeader();
NotifyDataSetChanged();

// After
var hadHeader = ItemsSource.HasHeader;
UpdateHasHeader();

if (hadHeader && !ItemsSource.HasHeader)
    NotifyItemRemoved(0);
else if (!hadHeader && ItemsSource.HasHeader)
    NotifyItemInserted(0);
else
    NotifyItemChanged(0);

Testing

Added comprehensive UI test CollectionViewHeaderFooterToggle that validates:

  • Header can be removed and re-added
  • Footer can be removed and re-added
  • Multiple toggle cycles work correctly for both header and footer

Platforms Affected

  • Windows ✅
  • Android ✅

Related Issues

Fixes issue reported in: [Windows/Android] Header/Footer re-add and removal issues in CollectionView

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • scanning-api.github.com
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node --enable-source-maps /home/REDACTED/work/_temp/copilot-developer-action-main/dist/index.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>[Windows/Android] Header/Footer re-add and removal issues in CollectionView</issue_title>
<issue_description>### Description

When the header/footer are added in CollectionView, the following issues were observed on Windows and Android platforms:

Windows: When toggling the Header/Footer button to the null state and then enabling it again, the header or footer content is not added back

Android: The header and footer are not removed at runtime when set to null.

Actual Behavior:
Windows - the header or footer content is not added back.
Android retains the old header/footer even after setting them to null.

Expected Behavior:
On Windows: Header/Footer should be re-added.
On Android: Header/Footer should be removed properly when set to null.

Steps to Reproduce

public partial class MainPage : ContentPage
{
    private CollectionView _collectionView;
    private Button _headerButton;
    private Button _footerButton;

	public MainPage()
	{
        //InitializeComponent();
		var grid = new Grid
        {
            Margin = new Thickness(20),
            RowDefinitions =
            {
                new RowDefinition { Height = GridLength.Auto },
                new RowDefinition { Height = GridLength.Auto },
                new RowDefinition { Height = GridLength.Star }
            }
        };



        // Header Label
        var headerLabel = new Label
		{
			Text = "Test for CollectionView empty view positioning",
			AutomationId = "HeaderLabel",
        };
        Grid.SetRow(headerLabel, 0);
        grid.Children.Add(headerLabel);

        // Control buttons
        var buttonGrid = new Grid
        {
            ColumnDefinitions =
            {
                new ColumnDefinition { Width = GridLength.Star },
                new ColumnDefinition { Width = GridLength.Star }
            },
            Margin = new Thickness(0, 10)
        };

        var toggleHeaderButton = new Button
        {
            AutomationId = "ToggleHeaderButton",
            Text = "Remove Header",
        };
        toggleHeaderButton.Clicked += OnToggleHeaderClicked;
        Grid.SetColumn(toggleHeaderButton, 0);
        buttonGrid.Children.Add(toggleHeaderButton);

        var toggleFooterButton = new Button
        {
            AutomationId = "ToggleFooterButton", 
            Text = "Remove Footer",
        };
        toggleFooterButton.Clicked += OnToggleFooterClicked;
        Grid.SetColumn(toggleFooterButton, 1);
        buttonGrid.Children.Add(toggleFooterButton);

        Grid.SetRow(buttonGrid, 1);
        grid.Children.Add(buttonGrid);

        // CollectionView
        _collectionView = new CollectionView
        {
			AutomationId = "CollectionView",
            ItemsSource = Array.Empty<string>(), // empty array to trigger EmptyView
            ItemTemplate = new DataTemplate(() =>
            {
                return new Label
                {
                    TextColor = Colors.Black,
                    FontSize = 16,
                    // This binding works with string items
                    BindingContext = "{Binding .}" 
                };
            })
        };

        // EmptyView
        _collectionView.EmptyView = new Label
        {
            AutomationId = "EmptyViewLabel",
            BackgroundColor = Color.FromArgb("#FFE40606"),
            Text = "EmptyView: This should show when no data.",
        };

        // Initial header
        _headerButton = new Button
        {
            AutomationId = "CollectionViewHeader",
            BackgroundColor = Colors.LightBlue,
            Text = "Header: Click me to verify EmptyView position",
        };
        _collectionView.Header = _headerButton;

        // Initial footer (null)
        _footerButton = new Button
        {
            AutomationId = "CollectionViewFooter",
            BackgroundColor = Colors.LightCoral,
            Text = "Footer: Click me to verify EmptyView position",
        };
        _collectionView.Footer = _footerButton

        Grid.SetRow(_collectionView, 2);
        grid.Children.Add(_collectionView);

        // Set ContentPage content
        Content = grid;
	}

    private void OnToggleHeaderClicked(object? sender, EventArgs e)
    {
        var button = (Button)sender!;
        
        if (_collectionView.Header != null)
        {
            _collectionView.Header = null;
            button.Text = "Add Header";
        }
        else
        {
            _collectionView.Header = _headerButton;
            button.Text = "Remove Header";
        }
    }

    private void OnToggleFooterClicked(object?  sender, EventArgs e)
    {
        var button = (Button)sender!;
        
        if (_collectionView.Footer != null)
        {
            _collectionView.Footer = null;
            button.Text = "Add Footer";
        }
        else
        {
            _collectionView.Footer = _footerButton;
         ...

</details>

Fixes dotnet/maui#31911

<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for you](https://github.com/dotnet/maui/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.

Windows: Ensure HeaderTemplate/FooterTemplate is cleared when set to null, and check if view is already a child before adding again.
Android: Use NotifyItemInserted/NotifyItemRemoved instead of NotifyDataSetChanged for proper item management.

Co-authored-by: sheiksyedm <[email protected]>
Copilot AI changed the title [WIP] Fix header and footer re-add/removal issues in CollectionView Fix CollectionView Header/Footer Toggle Issues on Windows and Android Oct 23, 2025
Copilot AI requested a review from sheiksyedm October 23, 2025 16:53
Copilot finished work on behalf of sheiksyedm October 23, 2025 16:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants