dotnet/maui

The Picker is still binding to the property and reacts to data changes after the page is closed.

Open

#33307 opened on Dec 28, 2025

View on GitHub
 (6 comments) (1 reaction) (0 assignees)C# (23,245 stars) (1,951 forks)batch import
area-controls-pickergood first issuepartner/syncfusionplatform/androidplatform/windowss/triageds/verifiedt/bug

Description

Description

I needed to write a solution where the user adds some elements in one window and assigns them to other elements in another window. The user can return to the first window and change them (rename, delete), and these changes should be reflected on the second page. When deleting a selected item, set null. When deleting an item from above or below the selected item, it should not cause problems. However, when I change the order of the elements on the first page, a setter is triggered in the elements of the second page, which puts the element at the same index, which is typical of the Picker element and its and its broken behavior described in issue #29235, although that page is closed and and not in the NavigationStack.

You can check the option without the picker element by clicking the button "Test without picker", where this error does not occur.

https://github.com/user-attachments/assets/ff2e25e1-9d4d-496d-b3d2-072d30b67b72

Steps to Reproduce

  1. Create a .Net MAUI project
  2. Change App.xaml.cs to:
namespace MauiApp1
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();

            MainPage = new NavigationPage(new MainPage())
            {
                BarBackgroundColor = Colors.Black,
                BarTextColor = Colors.White
            };
        }
    }
}
  1. Change MainPage.xaml to:
<?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"
             x:Class="MauiApp1.MainPage"
             Title="MainPage">
    <Grid>
        <HorizontalStackLayout Spacing="100" HorizontalOptions="Center">
            <Button x:Name="buttonPage1" Text="Page1" HeightRequest="180" WidthRequest="180" Clicked="buttonPage1_Clicked"/>
            <Button x:Name="buttonPage2" Text="Page2" HeightRequest="180" WidthRequest="180" Clicked="buttonPage2_Clicked"/>
            <Button x:Name="buttonTest" Text="Test without picker" HeightRequest="180" WidthRequest="180" Clicked="buttonTest_Clicked"/>
        </HorizontalStackLayout>
    </Grid>
</ContentPage>

4.Change MainPage.xaml.cs to:

namespace MauiApp1
{
    public partial class MainPage : ContentPage
    {
        ClassA mainData;
        public MainPage()
        {
            InitializeComponent();
            mainData = new();
        }
        private async void buttonPage1_Clicked(object sender, EventArgs e)
        {
            await Navigation.PushAsync(new Page1(mainData));
        }
        private async void buttonPage2_Clicked(object sender, EventArgs e)
        {
            await Navigation.PushAsync(new Page2(mainData));
        }

        private void buttonTest_Clicked(object sender, EventArgs e)
        {
            ClassA data = new();

            ClassB itemB1 = new ClassB();
            itemB1.id = data.id_counter++;
            itemB1.name = "itemB1";
            data.itemsB.Add(itemB1);

            ClassB itemB2 = new ClassB();
            itemB2.id = data.id_counter++;
            itemB2.name = "itemB2";
            data.itemsB.Add(itemB2);

            ClassB itemB3 = new ClassB();
            itemB3.id = data.id_counter++;
            itemB3.name = "itemB3";
            data.itemsB.Add(itemB3);

            ClassC itemC1 = new ClassC();
            itemC1.id = data.id_counter++;
            foreach (var itemB in data.itemsB)
                itemC1.itemsB.Add((ClassB)itemB.Clone());
            data.itemsC.Add(itemC1);

            itemC1.selected_item = itemB2;

            foreach (var itemC in data.itemsC)
            {
                for (int i = 0; i < itemC.itemsB.Count; i++)
                {
                    if (itemC.itemsB[i].id == itemB2.id)
                    {
                        itemC.itemsB.RemoveAt(i);
                    }
                }
            }

            data.itemsB.Remove(itemB2);

            if(itemC1.selected_item == null)
            {
                string text = "selected_process == null, and setter was not called ^_^";
            }
        }
    }
}
  1. Set a debug breakpoint on line 62
  1. Create a class "ClassA":
using System.Collections.ObjectModel;

namespace MauiApp1
{
    public class ClassA
    {
        public int id_counter {  get; set; }
        public ObservableCollection<ClassB> itemsB { get; set; } = new();
        public ObservableCollection<ClassC> itemsC { get; set; } = new();
    }
}
  1. Create a class "ClassB":
namespace MauiApp1
{
    public class ClassB
    {
        public int id {  get; set; }
        public string name {  get; set; }
        public bool isSelected { get; set; }
        public object Clone()
        {
            return MemberwiseClone();
        }
    }
}
  1. Create a class "ClassC":
using System.Collections.ObjectModel;

namespace MauiApp1
{
    public class ClassC
    {
        public int id {  get; set; }
        public string name {  get; set; }
        public ObservableCollection<ClassB> itemsB { get; set; } = new();
        public ClassB selected_item
        {
            get
            {
                for (var i = 0; i < itemsB.Count; i++)//returning the first selected
                {
                    if (itemsB[i].isSelected) return itemsB[i];
                }
                return null;//else null
            }
            set
            {
                if (value == null)//For third-party controls with a clear button, or a custom workaround (why not basic functionality?)
                {
                    for (var i = 0; i < itemsB.Count; i++) itemsB[i].isSelected = false;
                }
                else
                {
                    for (var i = 0; i < itemsB.Count; i++)
                    {
                        if (itemsB[i].name == value.name)
                        {
                            itemsB[i].isSelected = true;
                        }
                        else itemsB[i].isSelected = false;//Along the way, we remove the old selection
                    }
                }
            }
        }
    }
}
  1. Set a debug breakpoint on line 22
  1. Create a .NET MAUI ContenPage "Page1"
  2. Change Page1.xaml to:
<?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"
             x:Class="MauiApp1.Page1"
             Title="Page1">
    <Grid RowDefinitions="Auto,*,50" RowSpacing="11" Margin="10,10,10,20">
        <VerticalStackLayout Grid.Row="0" Spacing="10" VerticalOptions="End">
            <Grid ColumnDefinitions="*,60" ColumnSpacing="5">
                <Label Grid.Column="0" Text="Name" VerticalOptions="Center"/>
                <Label Grid.Column="1" Text="Delete" VerticalOptions="Center"/>
            </Grid>
            <BoxView Color="Black" HeightRequest="2" CornerRadius="20"/>
        </VerticalStackLayout>

        <CollectionView Grid.Row="1" x:Name="collectionView1" HorizontalOptions="Fill" BackgroundColor="WhiteSmoke">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <VerticalStackLayout Margin="1,0,1,0">
                        <Grid ColumnSpacing="5" Margin="0,8,0,8">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="60"/>
                            </Grid.ColumnDefinitions>
                            <Entry Grid.Column="0" Text="{Binding name}" Placeholder="Name" VerticalOptions="Center"/>
                            <Button x:Name="buttonDeleteRow" Grid.Column="1" Text="🗑" HeightRequest="51" WidthRequest="51" HorizontalOptions="Start" VerticalOptions="End" BackgroundColor="Red" Clicked="buttonDeleteRow_Clicked"/>
                        </Grid>
                        <BoxView Color="Black" HeightRequest="2" CornerRadius="20"/>
                    </VerticalStackLayout>
                </DataTemplate>
            </CollectionView.ItemTemplate>
            <!--EmptyView-->
            <CollectionView.EmptyView>
                <Label Text="No items to display" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
            </CollectionView.EmptyView>
        </CollectionView>
        <Button Grid.Row="2" x:Name="buttonAddRow" Text="Add items" HorizontalOptions="Start" Clicked="buttonAddRow_Clicked"/>
    </Grid>
</ContentPage>
  1. Change Page1.xaml.cs to:
using Microsoft.Maui.Controls;

namespace MauiApp1;

public partial class Page1 : ContentPage
{
    ClassA mainData;

    public Page1(ClassA mainData)
    {
        InitializeComponent();
        this.mainData = mainData;
        collectionView1.ItemsSource = this.mainData.itemsB;

    }
    private void buttonAddRow_Clicked(object sender, EventArgs e)
    {
        ClassB itemB1 = new ClassB();
        itemB1.id = mainData.id_counter++;
        itemB1.name = "itemB1";
        mainData.itemsB.Add(itemB1);

        ClassB itemB2 = new ClassB();
        itemB2.id = mainData.id_counter++;
        itemB2.name = "itemB2";
        mainData.itemsB.Add(itemB2);

        ClassB itemB3 = new ClassB();
        itemB3.id = mainData.id_counter++;
        itemB3.name = "itemB3";
        mainData.itemsB.Add(itemB3);
    }
    private async void buttonDeleteRow_Clicked(object sender, EventArgs e)
    {
        if (sender is Button button)
        {
            if (button.BindingContext is ClassB item)
            {
                bool result = await DisplayAlert("Comfirm the action", "Do you really want to delete the item \"" + item.name + "\" ?", "Yes", "No");
                if (result)
                {
                    RemoveInDependencies(item.id);
                    mainData.itemsB.Remove(item);
                }
            }
        }
    }
    void RemoveInDependencies(int id)
    {
        foreach (var itemC in mainData.itemsC)
        {
            for (int i = 0; i < itemC.itemsB.Count; i++)
            {
                if (itemC.itemsB[i].id == id)
                {
                    NavigationPage? navPage = Application.Current?.MainPage as NavigationPage;
                    var stack = navPage?.Navigation.NavigationStack;
                    int pageCount = navPage.Navigation.NavigationStack.Count;

                    var pagesTitle = string.Join("\n", stack);

                    itemC.itemsB.RemoveAt(i);
                    break;
                }
            }
        }
    }
}
  1. Set a debug breakpoint on line 62
  1. Create a .NET MAUI ContenPage "Page2"
  2. Change Page2.xaml to:
<?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"
             x:Class="MauiApp1.Page2"
             Title="Page2">
    <Grid RowDefinitions="Auto,*,50" RowSpacing="11" Margin="10,10,10,20">
        <VerticalStackLayout Grid.Row="0" Spacing="10" VerticalOptions="End">
            <Grid ColumnDefinitions="*,*,60" ColumnSpacing="5">
                <Label Grid.Column="0" Text="Name" VerticalOptions="Center" HorizontalOptions="Center"/>
                <Label Grid.Column="1" Text="ItemB" VerticalOptions="Center" HorizontalOptions="Center"/>
                <Label Grid.Column="2" Text="Delete" VerticalOptions="Center" HorizontalOptions="Center"/>
            </Grid>
            <BoxView Color="Black" HeightRequest="2" CornerRadius="20"/>
        </VerticalStackLayout>
        <CollectionView Grid.Row="1" x:Name="collectionView1" HorizontalOptions="Fill" BackgroundColor="WhiteSmoke">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <VerticalStackLayout Margin="1,0,1,0">
                        <Grid ColumnSpacing="5" ColumnDefinitions="*,*,60">
                            <Entry Grid.Column="0" Text="{Binding name}" Placeholder="Name" VerticalOptions="Center"/>
                            <Picker Grid.Column="1" ItemsSource="{Binding itemsB}" ItemDisplayBinding="{Binding name}" SelectedItem="{Binding selected_item}" TextColor="Black" TitleColor="Gray"/>
                            <Button Grid.Column="2" x:Name="buttonDeleteRow" Text="🗑" HeightRequest="51" WidthRequest="51" HorizontalOptions="Start" VerticalOptions="End" BackgroundColor="Red" Clicked="buttonDeleteRow_Clicked"/>
                        </Grid>
                        <BoxView Color="WhiteSmoke" HeightRequest="20"/>
                        <BoxView Color="Black" HeightRequest="2" CornerRadius="20"/>
                    </VerticalStackLayout>
                </DataTemplate>
            </CollectionView.ItemTemplate>
            <!--EmptyView-->
            <CollectionView.EmptyView>
                <Label Text="No items to display" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
            </CollectionView.EmptyView>
        </CollectionView>
        <Button Grid.Row="2" x:Name="buttonAddRow" Text="Add" HorizontalOptions="Start" Clicked="buttonAddRow_Clicked"/>
    </Grid>
</ContentPage>
  1. Change Page2.xaml.cs to:
namespace MauiApp1;

public partial class Page2 : ContentPage
{
    ClassA mainData;
    public Page2(ClassA mainData)
    {
        InitializeComponent();
        this.mainData = mainData;
        collectionView1.ItemsSource = this.mainData.itemsC;
    }
    private async void buttonDeleteRow_Clicked(object sender, EventArgs e)
    {
        if (sender is Button button)
        {
            if (button.BindingContext is ClassC item)
            {
                bool result = await DisplayAlert("Comfirm the action", "Do you really want to delete the item \"" + item.name + "\" ?", "Yes", "No");
                if (result)
                {
                    mainData.itemsC.Remove(item);
                }
            }
        }
    }
    private void buttonAddRow_Clicked(object sender, EventArgs e)
    {
        ClassC itemC = new ClassC();
        itemC.id = mainData.id_counter++;
        foreach (var itemB in mainData.itemsB)
            itemC.itemsB.Add((ClassB)itemB.Clone());
        mainData.itemsC.Add(itemC);
    }
}
  1. Launch the application
  2. Press the button "Page1"
  3. Press the button "Add items"
  4. Return to page "MainPage"
  5. Press the button "Page2"
  6. Press the button "Add"
  7. In the picker element, select the middle item and make sure the second item is selected.
  8. Return to page "Page1" and delete the first item (itemB1)
  9. Make sure "Page2" is closed
  10. Make "Step Into" and make sure that the "СlassC" object has a setter called, which sets the element at index 1 of the updated collection, triggered by the Picker
  11. Go back to "Page2" and make sure the item at index 1 is selected, not the previously selected item.

Link to public reproduction project repository

https://github.com/Nikita-181/MAUI_Picker_issue_1

Version with bug

10.0.20

Is this a regression from previous behavior?

No, this is something new

Last version that worked well

Unknown/Other

Affected platforms

Windows, Android

Affected platform versions

Windows 10 (10.0.19041.0), Android 16

Did you find any workaround?

Create a copy of the required data section when loading the ContentPage, fill the copy, override OnBackButtonPressed and make changes to the main data structure in it, as if working with a database

Relevant log output

Contributor guide