绑定到 ViewModel 在 Xamarin.Forms 中不起作用
Binding to the ViewModel not working in Xamarin.Forms
我正在尝试制作一个显示国家/地区列表的应用程序,当点击一个国家/地区时,它会显示该国家/地区的城市列表。
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; }
}
导航在 CountryPageViewModel.cs 中执行:
async void OnSelectedCountry(Country country)
{
if (country == null)
{
return;
}
await Shell.Current.GoToAsync($"{nameof(CitiesPage)}?{nameof(CitiesViewModel.Name)}={country.Name}");
}
城市加载到 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");
}
}
}
并且在 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>
但是“城市”页面中没有显示任何内容。我已将 CitiesPage.xaml.cs(后面的代码)中的 BindingContext 设置为 ViewModel。
我做错了什么?
对于可能有同样问题的人,如 xamarin 文档所述
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.
这里有更多关于 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.
我正在尝试制作一个显示国家/地区列表的应用程序,当点击一个国家/地区时,它会显示该国家/地区的城市列表。
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; }
}
导航在 CountryPageViewModel.cs 中执行:
async void OnSelectedCountry(Country country)
{
if (country == null)
{
return;
}
await Shell.Current.GoToAsync($"{nameof(CitiesPage)}?{nameof(CitiesViewModel.Name)}={country.Name}");
}
城市加载到 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");
}
}
}
并且在 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>
但是“城市”页面中没有显示任何内容。我已将 CitiesPage.xaml.cs(后面的代码)中的 BindingContext 设置为 ViewModel。
我做错了什么?
对于可能有同样问题的人,如 xamarin 文档所述
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.
这里有更多关于 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.