Home > OS >  Xamarin - Binding a Picker to MVVM command
Xamarin - Binding a Picker to MVVM command

Time:05-11

I'm trying to use the MVVM architecture on my Xamarin project but I can't quite understand it yet.

I have two entities CrCountry and CrCity and I want the user to be able to pick a country and then based on that country select a city. I want also the picker for the city to be "unavaliable" until the user picks a country.

In order to do that I need to know the item selected on my Picker so I can access my database and pass that value. How can I do that?

EDIT:

It works now with what I have, the only thing that isn't working is the IsEnabled property on my city picker. As I said I want the city picker to be disabled until a country is picked. I can't use SelectedIndex instead of SelectedItem as my CrCountry entity has a string as id and I need to get the selected item to filter cities by country. This is what I have:

My ViewModel

public class InvoiceDataViewModel : BindableObject
    {
        public InvoiceDataViewModel()
        {
            GetCountries();

        }

        private ApiService _apiService = new ApiService();
        private List<CrCountry> _countries;
        private List<CrCity> _cities;

        public List<CrCountry> Countries
        {
            get => _countries;
            set
            {
                _countries = value;
                OnPropertyChanged();
            }
        }

        public List<CrCity> Cities
        {
            get => _cities;
            set
            {
                _cities = value;
                OnPropertyChanged();

            }
        }

        private CrCountry _selectedCountry;
        public CrCountry SelectedCountry
        {
            get => _selectedCountry;
            set
            {
                _selectedCountry = value;
                OnPropertyChanged();
                UpdateCitiesList();
            }
        }

        private CrCity _selectedCity;
        public CrCity SelectedCity
        {
            get => _selectedCity;
            set
            {
                _selectedCity = value;
                OnPropertyChanged();
            }
        }

        private async void GetCountries()
        {
            // call database here
            var url = string.Concat(Constants.UrlCountry, "?PageSize=", 249);

            var countries = await _apiService.GetCountries(url, CurrentPropertiesService.GetToken());

            Countries = countries.Data;
        }

        private async void UpdateCitiesList()
        {
            var adios = _selectedCountry;

            if(_selectedCountry != null)
            {
                var url = string.Concat(Constants.UrlCity, "?IdCountry=", _selectedCountry.IdCountry,"&PageSize=",120);

                var cities = await _apiService.GetCitiesByCountry(url, CurrentPropertiesService.GetToken());

                Cities = cities.Data;
            }

           
        }

My Pickers on my View content page

<StackLayout Grid.Column="0" Grid.Row="3" Orientation="Vertical" HorizontalOptions="Fill">
                        <Label>País:</Label>
                            <Picker x:Name="pickerCountry" Title="Seleccione" 
                                    ItemDisplayBinding="{Binding Country}" 
                                    ItemsSource="{Binding Countries}" 
                                    SelectedItem="{Binding SelectedCountry, Mode=TwoWay}"
                                     />
                    </StackLayout>

                    <StackLayout Grid.Column="0" Grid.Row="4" Orientation="Vertical" HorizontalOptions="Fill">
                        <Label>Ciudad:</Label>
                            <Picker x:Name="pickerCity" Title="Seleccione" 
                                ItemsSource="{Binding Cities}" 
                                ItemDisplayBinding="{Binding City}" 
                                SelectedItem="{Binding SelectedCity, Mode=TwoWay}" >
                                <Picker.Triggers>
                                    <DataTrigger TargetType="Picker" Binding="{Binding Source={x:Reference pickerCountry},Path=SelectedItem}" Value="">
                                        <Setter Property="IsEnabled" Value="False"/>
                                    </DataTrigger>
                                </Picker.Triggers>
                            </Picker>
                    </StackLayout>

Please help I'm really lost with MVVM architecture. Thanks.

EDIT:

I finally achieved what I wanted I used the IsEnabled property. I followed this link: How can I disable DatePicker from viewmodel WPF MVVM

CodePudding user response:

A bit of a complex one to get started with MVVM but here is the approach I would use:

Enable/Disable Cities: I would use Xamarin triggers.

Your code will look something like this:

<StackLayout Grid.Column="0" Grid.Row="3" Orientation="Vertical" HorizontalOptions="Fill">
    <Label>Country:</Label>
    <Picker x:Name="pickerCountry" Title="Select one" ItemDisplayBinding="{Binding Name}" ItemsSource="{Binding Countries}" SelectedIndexChanged="PickerCountry_SelectedIndexChanged" SelectedIndex="{Binding SelectedCountryIndex}"/>
</StackLayout>

<StackLayout Grid.Column="0" Grid.Row="4" Orientation="Vertical" HorizontalOptions="Fill">
    <Label>City:</Label>
    <Picker x:Name="pickerCity" Title="Select one" ItemsSource="{Binding .}" ItemDisplayBinding="{Binding City}" SelectedIndexChanged="PickerCity_SelectedIndexChanged">
        <Picker.Triggers>
             <DataTrigger TargetType="Picker" Binding="{Binding Source={x:Reference pickerCountry},Path=SelectedIndex}" Value="-1">
                 <Setter Property="IsEnabled" Value=False/>
             </DataTrigger>
        </Picker.Triggers>
    </Picker>
</StackLayout>

When the selected index is -1 (i.e. the default value and nothing is selected) then it will disable the countries picker.

Update List of Cities

Once a country is selected you want to make a call to your database to get all the cities. You will want to bind to SelectedIndex or SelectedItem in your ViewModel. In this example I'll use the index:

private int _selectedCountryIndex
public int SelectedCountryIndex
{
    get => _selectedCountryIndex;
    set
    {
        _selectedCountryIndex = value;
        OnPropertyChanged();
        UpdateCitiesList();
    }
}

When the country index changes, you call a method UpdateCititesList() where you can update the list of cities in that country

So you may want to look at having a loading animation here so that users can't change the country while you are retrieving the cities, or you may want to have a different strategy. Just think about it a bit.

  • Related