thomasgalliker / Plugin.SegmentedControl.Maui

Segmented control for .NET MAUI apps
MIT License
10 stars 1 forks source link

[Bug] SelectedItem Two-Way binding source not changing #19

Closed gavingreenhorn closed 4 weeks ago

gavingreenhorn commented 4 weeks ago

Description

SelectedItem Two-Way binding doesn't seem to work, source is not updated upon selecting different segment in SegmentedControl.

Steps to Reproduce

Bound ItemsSource, SelectedItem and 'OnChanged' method are defined in VM like that:

using CommunityToolkit.Mvvm.ComponentModel;
...
        [ObservableProperty]
        ObservableCollection<SoundTypeOption> soundTypeOptions = [
            new SoundTypeOption { Latin = "", Native= "" },
            new SoundTypeOption { Latin = "", Native= "" },
            new SoundTypeOption { Latin = "", Native= "" }
        ];
...    
       [ObservableProperty]
       SoundTypeOption selectedSoundTypeOption;
...
        partial void OnSelectedSoundTypeOptionChanged(SoundTypeOption value)
        {
            if (value == null)
            {
                do_something;
            }
            else do_something_else;
        }

ContentView containing SegmentedControl is defined like that:

<ControlTemplate x:Key="Vowels">
    <Grid RowDefinitions="Auto,680">
        <s:SegmentedControl Margin="10" ItemsSource="{Binding SoundTypeOptions}" SelectedItem="{Binding SelectedSoundTypeOption, Mode=TwoWay}" TextPropertyName="Native"/>
        <CollectionView ... />
    </Grid>
</ControlTemplate>

If SegmentControl is replaced with Picker, selected item source changes as expected upon selecting different items in Picker.

<Picker
    Grid.Row="0"
    Title="Sound duration"
    ItemsSource="{Binding SoundTypeOptions}"
    SelectedItem="{Binding SelectedSoundTypeOption, Mode=TwoWay}"
    ItemDisplayBinding="{Binding Native}"/>

OnSelectedSoundTypeOptionChanged is not triggered upon selecting another segment.

Expected Behavior

Selected item source changing, , OnChanged method is triggered.

Actual Behavior

Selected item source is not changing, OnChanged method is not triggered.

Basic Information

Screenshots

Reproduction Link

thomasgalliker commented 4 weeks ago

Did you have a look at the SegmentedControlDemoApp in this repository? Test3Page + Test3ViewModel demonstrate SelectedItem bindings.

I adjusted Test3Page.xaml with code that is similar to what you posted in this issue. Have a look:

<s:SegmentedControl 
    Margin="10" 
    ItemsSource="{Binding Countries}" 
    SelectedItem="{Binding SelectedCountry, Mode=TwoWay}" 
    TextPropertyName="EnglishName"/>

And in the viewmodel I have following property. The setter is hit whenever a SelectedItem is updated.

public CountryItemViewModel SelectedCountry
{
    get => this.selectedItem;
    set => this.SetProperty(ref this.selectedItem, value);
}

Make sure the property's setter is public. I'm not very familiar with this code-generated properties. Check if it also doesn't work with manual code (non-generated). Also, maybe SoundTypeOption could be the source of the problem. You're using 3 exactly identical objects. Maybe SelectedItem does recognize them as same and does not update SelectedItem property?

thomasgalliker commented 4 weeks ago

Can you share the implementation of SoundTypeOption? Is there anything like override Equals or IEquitable<> implemented?

gavingreenhorn commented 4 weeks ago

Hi, thanks for your quick response.

This is a very simple class defined like that, serving to back selection items.

public class SoundTypeOption
{
    public string Latin { get; set; }
    public string Thai { get; set; }
}

I have replaced the automatically generated properties with explicit one:

private SoundTypeOption selectedSoundTypeOption;
public SoundTypeOption SelectedSoundTypeOption
{
    get => this.selectedSoundTypeOption;
    set {
        this.SetProperty(ref this.selectedSoundTypeOption, value);
        ...
    }
}

Yet the setter is only hit when property is assigned from code behind (e.g. SelectedSoundTypeOption = SoundTypeOptions[0]; ), but not on UI taps.

I also tried to bind to selectedItem from another control and changed mode to OneWay, but it's not showing that selection changes either.

image image

I guess I'll try to build up from your working example and see where it breaks. I think it might be due to the control being defined within a content template that is injected in the view (however, it works with the Picker somehow). Thanks!

thomasgalliker commented 4 weeks ago

Very strange. Yes, I saw you're using the SegmentedControl inside a ControlTemplate. Shouldn't you use {TemplatedBinding ...} instead if {Binding ...} in this situation? I agree, it's strange that it works with the Picker but not with the SegmentedControl.

If you create a very minimal sample app on a public github repo, I can clone it and try to debug it.

gavingreenhorn commented 4 weeks ago

I've made a simple example and... of course it was working. :) Changed everything back in the main project to exactly mirror the reproduction and it still wasn't working. Eventually turned out that something in the other package was causing an issue. Had to uninstall both this one and Plugin.Maui.SegmentedControl that I tried using before. Re-installed your package after this. Everything is working fine after that. I suppose there could be some namespace conflicts? Although the compiler didn't complain about a thing. In any case, apologies for taking your time, and thank you for the package.

thomasgalliker commented 4 weeks ago

That sounds indeed very weird. Having two visual elements with the same name in the same xaml should work - as long as you have different namespace aliases. But yeah who know what MAUI mixes up in the background.... anyway. Hope it works now. Otherwise, let me know 👍🏻