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.