I added a text entry to Monkeys[0].Name (for the baboon) and I can change the .Name and it updates the codebehind and if I navigate to the Baboon details page it has the new Name, but the Monkeys page never updates the name of the baboon. What do I need to change to get this binding to work properly?
Here is my slight modification:
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:Monkey">
<Grid Padding="10">
<Frame HeightRequest="125" Style="{StaticResource CardView}">
<Frame.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MonkeysViewModel}}, Path=GoToDetailsCommand}" CommandParameter="{Binding .}" />
</Frame.GestureRecognizers>
<Grid Padding="0" ColumnDefinitions="125,*">
<Image
Aspect="AspectFill"
HeightRequest="125"
Source="{Binding Image}"
WidthRequest="125" />
<VerticalStackLayout Grid.Column="1" Padding="10">
<Label Style="{StaticResource LargeLabel}" Text="{Binding Name, Mode=TwoWay}" />
<Label Style="{StaticResource MediumLabel}" Text="{Binding Location}" />
</VerticalStackLayout>
</Grid>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<!-- MY ENTRY ADDITIONS I ALSO PUT TWOWAY FOR THE DATATTEMPLATE ABOVE -->
<VerticalStackLayout Grid.Row="0" Grid.Column="2" >
<Label Text="NewBaboonName" Margin ="0,10,0,0" HeightRequest="20" WidthRequest="200" VerticalOptions="End" />
<Entry Text="{Binding Monkeys[0].Name, Mode=TwoWay}" Margin ="2" HeightRequest="50" WidthRequest="200" Placeholder="ChangeName" />
</VerticalStackLayout>**
The Monkeys page passes the details page the Model. But it is more that the Monkeys[0].Name does not update on the Monkeys page. The whole application is by james montemagno. It can be downloaded at https://github.com/dotnet-presentations/dotnet-maui-workshop (Use Part 6 for the final code).
All I did was check the binding by adding the following line to the MainPage.xaml , which is bound to viewmodel:MonkeysViewModel.
<Entry Text="{Binding Monkeys[0].Name, Mode=TwoWay}" Margin ="2" HeightRequest="50" WidthRequest="200" Placeholder="ChangeName" />
I just find the binding weird in that it does update the Monkeys[0].Name on the Monkeys detail page, but not on the monekys page(MainPage). Another bizarre "bug" is that if you change the Mode on the entry from TwoWay to OneWay, or OneWay to TwoWay the Monkeys[0].Name field will update on the monkeys page(MainPage).
I'm not sure if the binding is in "developement" mode or this is the way it should work. Though I would think if the value in the object is updated in the code behind it should update on the UI (with TwoWay at least).
Copy of the MonkeysViewModel:
using MonkeyFinder.Services;
namespace MonkeyFinder.ViewModel;
public partial class MonkeysViewModel : BaseViewModel
{
public ObservableCollection<Monkey> Monkeys { get; } = new();
MonkeyService monkeyService;
IConnectivity connectivity;
IGeolocation geolocation;
public MonkeysViewModel(MonkeyService monkeyService, IConnectivity connectivity, IGeolocation geolocation)
{
Title = "Monkey Finder";
this.monkeyService = monkeyService;
this.connectivity = connectivity;
this.geolocation = geolocation;
}
[ObservableProperty]
bool isRefreshing;
[RelayCommand]
async Task GetMonkeysAsync()
{
if (IsBusy)
return;
try
{
if (connectivity.NetworkAccess != NetworkAccess.Internet)
{
await Shell.Current.DisplayAlert("No connectivity!",
$"Please check internet and try again.", "OK");
return;
}
IsBusy = true;
var monkeys = await monkeyService.GetMonkeys();
if(Monkeys.Count != 0)
Monkeys.Clear();
foreach(var monkey in monkeys)
Monkeys.Add(monkey);
}
catch (Exception ex)
{
Debug.WriteLine($"Unable to get monkeys: {ex.Message}");
await Shell.Current.DisplayAlert("Error!", ex.Message, "OK");
}
finally
{
IsBusy = false;
IsRefreshing = false;
}
}
[RelayCommand]
async Task GoToDetails(Monkey monkey)
{
if (monkey == null)
return;
await Shell.Current.GoToAsync(nameof(DetailsPage), true, new Dictionary<string, object>
{
{"Monkey", monkey }
});
}
[RelayCommand]
async Task GetClosestMonkey()
{
if (IsBusy || Monkeys.Count == 0)
return;
try
{
// Get cached location, else get real location.
var location = await geolocation.GetLastKnownLocationAsync();
if (location == null)
{
location = await geolocation.GetLocationAsync(new GeolocationRequest
{
DesiredAccuracy = GeolocationAccuracy.Medium,
Timeout = TimeSpan.FromSeconds(30)
});
}
// Find closest monkey to us
var first = Monkeys.OrderBy(m => location.CalculateDistance(
new Location(m.Latitude, m.Longitude), DistanceUnits.Miles))
.FirstOrDefault();
await Shell.Current.DisplayAlert("", first.Name + " " +
first.Location, "OK");
}
catch (Exception ex)
{
Debug.WriteLine($"Unable to query location: {ex.Message}");
await Shell.Current.DisplayAlert("Error!", ex.Message, "OK");
}
}
}