Reputation: 13
I am navigating to a detail page, passing Id as a parameter, retrieving it and looking up the detail model from a service. That part works fine. However, in the page the model object within the ViewModel is not updated even though it is ObservableProperty.
Defined DI:
CreateRoutePageMapping<MembersPage, MembersViewModel>(),
CreateRoutePageMapping<MemberPage, MemberViewModel>(),
Defined model:
public partial class MemberModel : ObservableObject
{
[ObservableProperty]
private string firstName;
[ObservableProperty]
private string lastName;
public MemberModel()
{
FirstName = String.Empty;
LastName = String.Empty;
}
}
Defined ViewModel:
public partial class MemberViewModel : BaseViewModel, IQueryAttributable
{
readonly IMembersService _membersService;
readonly IStringLocalizer _localizer;
[ObservableProperty]
int id;
[ObservableProperty]
MemberModel member = new();
partial void OnIdChanged(int value)
{
Task.Run(async () => await GetMemberAsync(id: Id));
}
public MemberViewModel(
IMembersService membersService,
IConnectivity connectivity,
IDialogService dialogService,
IStringLocalizer<ProjectStrings> localizer
) : base(connectivity, dialogService, localizer)
{
_membersService = membersService;
_localizer = localizer;
}
[RelayCommand]
async Task GetMemberAsync(int id)
{
if (IsBusy)
return;
try
{
if (!(await HasInternetAccessAsync()))
return;
IsBusy = true;
Member = await _membersService.GetMemberAsync(id: id);
//OnPropertyChanged(nameof(Member));
OnPropertyChanged(nameof(Member.FirstName));
OnPropertyChanged(nameof(Member.LastName));
}
catch (Exception ex)
{
Debug.WriteLine($"Error retrieving member: {ex.Message}");
}
finally
{
IsBusy = false;
}
}
void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query)
{
int id = (int)query["id"];
Id = id;
OnPropertyChanged(nameof(Id));
}
}
Defined Page:
using static CommunityToolkit.Maui.Markup.GridRowsColumns;
public partial class MemberPage : BaseContentPage<MemberViewModel>
{
public MemberPage(MemberViewModel memberViewModel) :
base(viewModel: memberViewModel, pageTitle: memberViewModel.Title)
{
Style = AppStyles.ContentPageStyle;
Content = new ScrollView()
{
Content = new VerticalStackLayout()
{
VerticalOptions = LayoutOptions.Start,
HorizontalOptions = LayoutOptions.Center,
Children =
{
new Label()
{
}
.Bind(Label.TextProperty,
static (MemberViewModel vm) => vm.Member.FirstName, mode: BindingMode.OneWay),
new Label()
{
}
.Bind(Label.TextProperty,
static (MemberViewModel vm) => vm.Member.LastName, mode: BindingMode.OneWay)
}
}
};
}
}
In the ViewModel, if this line is enabled: //OnPropertyChanged(nameof(Member)); then the updates are not reflected in the UI.
However, if I specify the attributes in the model explicitly:
OnPropertyChanged(nameof(Member.FirstName));
OnPropertyChanged(nameof(Member.LastName));
then they are reflected in the UI. Why does the OnPropertyChanged(nameof(Member)) not work?
Upvotes: 0
Views: 362
Reputation: 13
I was able to get this to work using this binding from the community toolkit examples. It is verbose but avoids the explicit OnPropertyChanged.
new Label()
{
}
.Bind(
targetProperty: Label.TextProperty,
getter: static (MemberViewModel vm) => vm.Member.FirstName,
handlers: new (Func<MemberViewModel, object?>, string)[]
{
(vm => vm, nameof(MemberViewModel.Member)),
(vm => vm.Member, nameof(MemberViewModel.Member.FirstName)),
},
setter: static (MemberViewModel vm, string text) => vm.Member.FirstName = text),
Upvotes: 0
Reputation: 13879
However, if I specify the attributes in the model explicitly: OnPropertyChanged(nameof(Member.FirstName)); OnPropertyChanged(nameof(Member.LastName)); then they are reflected in the UI. Why does the OnPropertyChanged(nameof(Member)) not work?
From the code you shared, I couldn't reproduce this problem.
But you can first recheck the bind way you used.
You can refer to the following code:
<Label Text="{Binding Member.FirstName}"/>
<Label Text="{Binding Member.LastName}"/>
Then we don't need call OnPropertyChanged
, we can just do as follows.
Once the value of Member
changes, the UI will refresh itself.
try
{
if (!(await HasInternetAccessAsync()))
return;
IsBusy = true;
Member = await _membersService.GetMemberAsync(id: id);
//OnPropertyChanged(nameof(Member));
//OnPropertyChanged(nameof(Member.FirstName));
//OnPropertyChanged(nameof(Member.LastName));
}
Upvotes: 0