简体   繁体   中英

Binding to the ViewModel not working in Xamarin.Forms

I'm trying to make an app that shows a list of countries, when a country is clicked it shows a list of cities that are in that country.

Country.cs:

public class Country {
  public string Name { get; set; }
  public ImageSource Image { get; set; }
  public IList<City> Cities { get; set; }
}

City.cs:

public class City
{
    public string Name { get; set; }
}

Navigation is performed in CountryPageViewModel.cs through:

async void OnSelectedCountry(Country country)
    {
        if (country == null)
        {
            return;
        }

        await Shell.Current.GoToAsync($"{nameof(CitiesPage)}?{nameof(CitiesViewModel.Name)}={country.Name}");
    }

Cities are loaded into the CitiesViewModel.cs:

[QueryProperty(nameof(Name), nameof(Name))]
public class CitiesViewModel : BaseViewModel
{
    private string countryName;

    public IList<City> CityList { get; set; }

    public string Name
    {
        get
        {
            return countryName;
        }

        set
        {
            countryName = value;
            LoadCities(value);
        }
    }

    public async void LoadCities(string countryName)
    {
        try
        {
            Country country = await DataStore.GetItemAsync(cityName);
            IList<City> cities = country.Cities;
            CityList = cities;
        }
        catch (Exception)
        {
            Console.WriteLine("Impossible to load cities");
        }
    }
}

And in the XAML (CitiesPage.xaml):

<CollectionView x:Name="citiesCollectionView"  x:DataType="viewmodels:CitiesViewModel"
            ItemsSource="{Binding CityList}"
            EmptyView="Impossible to load cities" >
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Grid Padding="5" x:DataType="models:City">
                <Frame Style="{StaticResource CityCard}">
                    <Grid RowDefinitions="Auto,Auto"
                          ColumnDefinitions="Auto" >
                        <Label Grid.Column="1" 
                            Text="{Binding Name}" 
                            FontAttributes="Bold"
                            VerticalTextAlignment="Center"
                            Padding="10"
                            HeightRequest="50" />
                    </Grid>
                </Frame>
            </Grid>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

But nothing is showing up in the Cities page. I've set BindingContext in the CitiesPage.xaml.cs (code behind) to the ViewModel.

What am I doing wrong?

For who may have same problem, as xamarin document says

All view model and model classes that are accessible to a view should implement the INotifyPropertyChanged interface. Implementing this interface in a view model or model class allows the class to provide change notifications to any data-bound controls in the view when the underlying property value changes.

Here are more tips about PropertyChanged:

-Always raising a PropertyChanged event if a public property's value changes. Do not assume that raising the PropertyChanged event can be ignored because of knowledge of how XAML binding occurs.

Always raising a PropertyChanged event for any calculated properties whose values are used by other properties in the view model or model.

Always raising the PropertyChanged event at the end of the method that makes a property change, or when the object is known to be in a safe state. Raising the event interrupts the operation by invoking the event's handlers synchronously. If this happens in the middle of an operation, it might expose the object to callback functions when it is in an unsafe, partially updated state. In addition, it's possible for cascading changes to be triggered by PropertyChanged events. Cascading changes generally require updates to be complete before the cascading change is safe to execute.

Never raising a PropertyChanged event if the property does not change. This means that you must compare the old and new values before raising the PropertyChanged event.

Never raising the PropertyChanged event during a view model's constructor if you are initializing a property. Data-bound controls in the view will not have subscribed to receive change notifications at this point.

Never raising more than one PropertyChanged event with the same property name argument within a single synchronous invocation of a public method of a class. For example, given a NumberOfItems property whose backing store is the _numberOfItems field, if a method increments _numberOfItems fifty times during the execution of a loop, it should only raise property change notification on the NumberOfItems property once, after all the work is complete. For asynchronous methods, raise the PropertyChanged event for a given property name in each synchronous segment of an asynchronous continuation chain.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM