xamarin / Xamarin.Forms

Xamarin.Forms is no longer supported. Migrate your apps to .NET MAUI.
https://aka.ms/xamarin-upgrade
Other
5.62k stars 1.87k forks source link

Picker.SelectedItem reset to null during OnPropertyChanged #2751

Closed duzenko closed 3 years ago

duzenko commented 6 years ago

Description

This looks like a variant of #1879 but I think that this issue is a bug, not an enhancement request. The difference to that one is that even a basic call to OnPropertyChanged breaks picker selection, which to me sounds like a different scenario.

Steps to Reproduce

  1. Start the attached project on Android or UWP (iOS not tested).
  2. In about 1 second after page load, the second picker gets cleared.

Expected Behavior

Picker selection persists.

Actual Behavior

Picker is cleared.

Basic Information

Reproduction Link

asd.zip

pauldipietro commented 6 years ago

This does still occur with the reproduction on 3.1 but I'd leave this to someone else to determine what the expected behavior would be here.

dunlavy commented 6 years ago

Can we get this in? There an ETA?

It's definitely affecting my team's project.

Appreciate Xamarin and your efforts, guys! Been a pleasure to work with this and Prism.

dunlavy commented 6 years ago

Would definitely classify this as a bug and not an enhancement, BTW. Binding to a List<string> and raising OnPropertyChanged definitely kills the user's selected string value, which IMO is not anticipated behavior.

BradChase2011 commented 6 years ago

@dunlavy If you want to apply my PR to the forms source and copy the files over in the meantime then it should work out for ya.

dunlavy commented 5 years ago

Thanks! Lol, how is this not merged in yet?

duzenko commented 5 years ago

Somehow I'm not surprised. Terrible support like this is why I gave up on Xamarin.

dunlavy commented 5 years ago

I love Xamarin. What do you use? I'll never go back to Ionic, while NativeScript shows some promise and I've used it successfully in a major project.

I won't look a free gift-horse in the mouth too badly, so to speak, but this is a pretty blatant issue that's now a little over one year old. Hopefully it's closed soon!

duzenko commented 5 years ago

I love Xamarin. What do you use? I'll never go back to Ionic, while NativeScript shows some promise and I've used it successfully in a major project.

Flutter for now. At least they know where they're heading. Ionic? Lol, I want to go back to 2014.

dunlavy commented 5 years ago

Lol, I want to go back to 2014.

No you don't. :-) Xamarin is my steady go-to.

Looks like this is in progress thankfully.

nk54 commented 4 years ago

:/ still opened 2 years later... It's sad because this control is a basic... 99% of B2B app may (must?) use it...

I will have to create my own Picker to avoid such problems. I got my ItemsSource (an observable collection) raised with new items and I lose my SelectedItem even if the selected item is still in the items source.

duzenko commented 4 years ago

The funny thing, they have a PR that fixes this and many other bugs but they're too busy to merge it.

nk54 commented 4 years ago

The funny thing, they have a PR that fixes this and many other bugs but they're too busy to merge it.

Yeah funny as you say... So sad. They may focus on Shell kind of thing haha. Sometimes, I got the feeling that they are slowing everything, waiting people to move on Flutter because Xamarin bring no money. But they forget that if nobody use Xamarin, fewer people will buy a visual studio licence, fewer people will use Azure, fewer people will take a msdn subscription, fewer people will trust new tech from Microsoft (blazor). But that's not my business ^^

samhouts commented 3 years ago

This issue doesn't seem to have had any activity in a long time. We're working on prioritizing issues and resolving them as quickly as we can. To help us get through the list, we would appreciate an update from you to let us know if this is still affecting you on the latest version of Xamarin.Forms, since it's possible that we may have resolved this as part of another related or duplicate issue. If we don't see any new activity on this issue in the next 30 days, we'll evaluate whether this issue should be closed. Thank you!

Redth commented 3 years ago

Since we haven't heard from you in more than 30 days, we hope this issue is no longer affecting you. If it is, please reopen this issue and provide the requested information so that we can look into it further. Thank you!

duzenko commented 3 years ago

How do I reopen this issue exactly? Is there a button somewhere or ???

EmmanuelJego commented 3 years ago

Upgrading Xamarin.Forms to 5.x should fix this

PrimoDev23 commented 3 years ago

So we are also facing this issue at our company. When triggering OnPropertyChanged-Event it sets all SelectedItems to null. And yes, we could use a dirty fix but this issue isn't fixed since literally years...

Also it doesn't seem to be merged in 5.X...

MarcusCoding commented 3 years ago

@EmmanuelJego No, it isn't fixed in the current stable build of Xamarin.Forms 5 (v. 5.0.0.2012)

Minimal example using Xamarin.Forms 5.

After running PropertyChanged, the SelectedItem of the second combobox is null without any reason.

duzenko commented 3 years ago

@all this bug will receive no further attention from @xamarin please create a new bug and wait XX years for the bot to auto-close it

PrimoDev23 commented 3 years ago

@samhouts could you reopen this issue please? MVVM is important for many developers and not fixing this bug gives major issues in almost any projects that use mvvm.

dunlavy commented 3 years ago

Ditto. We have some kiosks running a Xamarin application and combo boxes are always forgetting their values while other controls work as expected. Although, to be fair, these kiosks are still using older Xamarin builds.

I hope this got fixed somewhere along the way via other merges.

PrimoDev23 commented 3 years ago

Ditto. We have some kiosks running a Xamarin application and combo boxes are always forgetting their values while other controls work as expected. Although, to be fair, these kiosks are still using older Xamarin builds.

I hope this got fixed somewhere along the way via other merges.

I can tell you that it isn't fixed yet. We even tried the latest service release since every version after pre5 had material issues (just the fact that such issues get into new major releases and many other issues we had to work around with dirty hacks made me start learning kotlin today) Maybe we can expect this to be fixed in the future but personally I don't even think this will be reopened.

PrimoDev23 commented 3 years ago

Since this seems to never get fixed I am providing my solution for working around it here. It's a custom picker that does exactly what the normal picker should do and it's working perfectly in our case.

`public class CustomPicker : Picker { public static readonly new BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof(string[]),typeof(CustomPicker), null, propertyChanged: ItemsSourceChanged);

    private static void ItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
    {
        CustomPicker picker = (CustomPicker)bindable;
        if(newValue is string[] newArray)
        {
            string[] oldArray = (string[])oldValue;

            string selected = picker.SelectedItem?.ToString();

            //If old or new is null we don't care about selecteditem
            //If the length is different we want the user to select again since new items came in
            if (oldArray == null || newArray == null || (oldArray.Length != newArray.Length))
            {
                picker.ItemsSource = newArray;
            }else
            {
                //Both are same length and not null
                bool different = false;
                for (int i = 0; i < newArray.Length; i++)
                {
                    if(newArray[i] != oldArray[i])
                    {
                        different = true;
                    }
                }

                if (different)
                {
                    picker.ItemsSource = newArray;
                }
            }

            if (selected != null && newArray?.Contains(selected) == true)
            {
                picker.SelectedItem = selected;
            }
        }
    }
}`

This just prevents resetting the ItemsSource when it hasn't changed and trying to reset the SelectedItem if possible (even if ItemsSource changed)

alexlogvin commented 2 years ago

Used a snippet from kbd>@PrimoDev23</kbd in the post above and improved it to be used with any kind of collection. Feel free to use it

using System.Collections;

using Xamarin.Forms;

namespace YourXamarinProject.Controls
{
    public class Picker : Xamarin.Forms.Picker
    {
        public static new readonly BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof(IList), typeof(Picker), null, propertyChanged: ItemsSourceChanged);

        private static void ItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
        {
            Picker picker = (Picker)bindable;
            if (newValue is IList newArray)
            {
                IList oldArray = (IList)oldValue;

                object selected = picker.SelectedItem;

                //If old or new is null we don't care about selecteditem
                //If the length is different we want the user to select again since new items came in
                if (oldArray == null || newArray == null || (oldArray.Count != newArray.Count))
                {
                    picker.ItemsSource = newArray;
                }
                else
                {
                    //Both are same length and not null
                    bool different = false;
                    for (int i = 0; i < newArray.Count; i++)
                    {
                        if (newArray[i] != oldArray[i])
                        {
                            different = true;
                        }
                    }

                    if (different)
                    {
                        picker.ItemsSource = newArray;
                    }
                }

                if (selected != null && newArray?.Contains(selected) == true)
                {
                    picker.SelectedItem = selected;
                }
            }
        }
    }
}