Why SelectedItem of ComboBox is 0 at first time while setting TwoWay binding onto source property?

148 views Asked by At

I wish to designate one item as selected from the ItemSource collection, so types are the same. The source property is set in VM's ctor and explicit property changed raised.

EDIT (according to StackOverflow guidelines):

UxSettingKeyValue's ctor:

SelectedItem = Setting.Available.FirstOrDefault(e => e.IsChecked);

(Setting.Available is IEnumerable<SettingState>)

VM's ctor:

var setting = _provider.MakeByKey(ClimateCondition.Pm25.AsKey(), ClimateCondition.Pm25);
var caption = _languageService.GetResource(setting.Key);
    
Pm25 = new UxSettingKeyValue(caption, setting);
    
SelectedItem = Pm25.SelectedItem;
     
RaisePropertyChanged(nameof(SelectedItem)); // It should not be neccessary since package <PropertyChanged.Fody 4.1.0> is installed. 

VM's SelectedItem property :

internal SettingState SelectedItem
{
    get;set;
}

XAML:

<ComboBox
    ItemsSource="{x:Bind ViewModel.Pm25.Setting.Available}"
    SelectedItem="{x:Bind ViewModel.SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    DisplayMemberPath="Caption"/>

Initially it looks like this :

enter image description here

Live Property Explorer:

enter image description here

The ItemSource seems to be properly initialized as the drop down values appear well;

enter image description here

(Currently all async operations rendered to synchronous for this debug purpose)

When I select an item on the combo box, the source property is set well, but initialy is 0 as you can see on Live Property Explorer.

EDIT as @Katana suggested to harness the Loaded event instead of ctor:

The call back is registered;

    <ContentDialog 
        x:Class="Weather.History.Mvvm.Views.SettingsDialog"
        xmlns:prismmvvm="using:Prism.Mvvm"
        xmlns:mycnv="using:Weather.History.Mvvm.ValueConverter"
        prismmvvm:ViewModelLocator.AutowireViewModel="True"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:myctr="using:Weather.History.UC"
        xmlns:myux="using:Weather.History.Mvvm.Model"
        xmlns:wctk="using:CommunityToolkit.WinUI.Controls"
        Loaded="OnContentDialogLoaded"
        PrimaryButtonText="Close"
        PrimaryButtonClick="OnClose">

The event handler :

    private async void OnContentDialogLoaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
    {
            await ViewModel.LoadSettingsAsync();
    }

VM's LoadSettingsAsync method to initialize properties for the combo boxes :

    internal Task LoadSettingsAsync()
    {
        var dispatcher = DispatcherQueue.GetForCurrentThread();

        return Task.Run(async () =>
        {
            var setting = _provider.MakeByKey(ClimateCondition.AirPressure.AsKey(), ClimateCondition.AirPressure);
            var caption = _languageService.GetResource(setting.Key);


            setting = _provider.MakeByKey(ClimateCondition.Pm25.AsKey(), ClimateCondition.Pm25);
            caption = _languageService.GetResource(setting.Key);

            dispatcher.EnqueueAsync(() => Pm25 = new UxSettingKeyValue(caption, setting), DispatcherQueuePriority.Normal);

            setting = _provider.MakeByKey(ClimateCondition.Pm10.AsKey(), ClimateCondition.Pm10);
            caption = _languageService.GetResource(setting.Key);

            dispatcher.EnqueueAsync(() => Pm10 = new UxSettingKeyValue(caption, setting), DispatcherQueuePriority.Normal);

            // ... 

            await SeekMyHomeAsync(dispatcher);
        });
    }

Adjusted the source properties in XAML file :

                    <wctk:SettingsCard Description="{x:Bind ViewModel.Pm25.Caption}"
                                       Header="PM 25"
                                       HeaderIcon="{winui3:FontIcon GlyphName=Accept}">
                        <ComboBox ItemsSource="{x:Bind ViewModel.Pm25.Setting.Available, Mode=OneWay}"
                                  SelectedItem="{x:Bind ViewModel.Pm25.SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                  DisplayMemberPath="Caption"/>
                    </wctk:SettingsCard>

But still the ItemSource is initialized only. The SelectedItem is 0 until after I myself select one.

2

There are 2 answers

6
Katana On

Change

SelectedItem="{x:Bind ViewModel.SelectedItem, Mode=OneTime}"

to

SelectedItem="{x:Bind ViewModel.SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
1
Andrew KeepCoding On

Here's a small block of sample code that:

  • populates a ComboBox with enum values
  • initializes the selected item

But, since I'm not familiar with Fody, I'm using the CommunityToolkit.Mvvm NuGet package instead.

MainPageViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using System;

namespace App1;

public enum SettingState
{
    State0,
    State1,
    State2,
}

// Make sure this class is "partial" for the "CommunitToolkit.Mvvm"'s source generators.
public partial class MainPageViewModel : ObservableObject
{
    [ObservableProperty]
    // An INotifyPropertyChanged implemented "SettingState" property will be auto-generated for you.
    private SettingState _settingState = SettingState.State2;

    public SettingState[] SettingStates { get; } = Enum.GetValues<SettingState>();
}

MainPage.xaml.cs

using Microsoft.UI.Xaml.Controls;

namespace App1;

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    public MainPageViewModel ViewModel { get; } = new();
}

MainPage.xaml

<Page
    x:Class="App1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:App1"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    mc:Ignorable="d">

    <Grid>
        <ComboBox
            ItemsSource="{x:Bind ViewModel.SettingStates}"
            SelectedItem="{x:Bind ViewModel.SettingState, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    </Grid>

</Page>