Не отображаются привязанные данные в полях ввода Xamarin, хотя все инстансы и свойства проинициализированы
При переходе на страницу просмотра и редактирования данных (PersonDetailsPage) в полях ввода ничего не отображается :
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamarinExploring.Views.PersonDetailsPage"
>
<StackLayout>
<StackLayout
x:Name="PersonStack"
>
<Label Text="Name" />
<Entry Text="{Binding currentlyBeingEditedExistingPersonData.Name}" FontSize="Medium" />
<Label Text="Email" />
<Entry Text="{Binding currentlyBeingEditedExistingPersonData.Email}" FontSize="Medium" />
<Label Text="Phone" />
<Entry Text="{Binding currentlyBeingEditedExistingPersonData.Phone}" FontSize="Medium" />
</StackLayout>
<!-- ... -->
</StackLayout>
</ContentPage>
В конструкторе PersonDetailsPage я убедился, что currentlyBeingEditedExistingPersonData проинициализировано успешно:
public partial class PersonDetailsPage : ContentPage {
public PeopleListViewModel PeopleListViewModel { get; private set; }
public PersonDetailsPage(PeopleListViewModel peopleListViewModel) {
InitializeComponent();
PeopleListViewModel = peopleListViewModel;
BindingContext = PeopleListViewModel;
// Всё О'K
Console.WriteLine(PeopleListViewModel.currentlyBeingEditedExistingPersonData);
Console.WriteLine(PeopleListViewModel.currentlyBeingEditedExistingPersonData.Name);
Console.WriteLine(PeopleListViewModel.currentlyBeingEditedExistingPersonData.Email);
Console.WriteLine(PeopleListViewModel.currentlyBeingEditedExistingPersonData.Phone);
}
}
Инициализирую я currentlyBeingEditedExistingPersonData в PeopleListViewModel следующим образом:
/* === Person viewing and editing =============================================================================== */
private PersonViewModel _selectedPerson;
/* [ Методология ] На данный момент этот класс полностью совпадает с 'PersonViewModel' , но в будущем это будет не так. */
public CurrentlyBeingEditedExistingPersonData currentlyBeingEditedExistingPersonData;
public class CurrentlyBeingEditedExistingPersonData : PersonViewModel {}
public PersonViewModel SelectedPerson {
get => _selectedPerson;
set {
if (_selectedPerson == value) return;
_selectedPerson = value;
OnPropertyChanged(nameof(SelectedPerson));
currentlyBeingEditedExistingPersonData = new CurrentlyBeingEditedExistingPersonData {
Name = _selectedPerson.Name,
Email = _selectedPerson.Email,
Phone = _selectedPerson.Phone
};
NavigationService.PushAsync(new PersonDetailsPage(this));
_selectedPerson = null;
}
}
На этом с вопросом всё, но я бы хотел дать некоторые комментарии, показывающие ход моих мыслей. Если Вы хотите посмотреть ещё какие-нибудь фрагменты кода этого тренировочного проекта, то вот ссылка на репозиторий c проектом в текущем состоянии.
Насчёт сеттера SelectedPerson
С точки зрения логики, мне этот SelectedPerson (SelectedItem в ListView) незачем. SelectedPerson - это ссылка на один из элементов коллекции public ObservableCollection<PersonViewModel> People { get; set; }, а поскольку нам нужна возможность прерывать редактирование данных с откатом изменений, то привязывать значения полей вода напрямую к свойствам SelectedPerson неправильно. Поэтому я создал дополнительное поле currentlyBeingEditedExistingPersonData.
CurrentlyBeingEditedExistingPersonData на данный момент полностью совпадает с PersonViewModel, но в будущем это будет не так (допустим, у PersonViewModel свойства Name станет обязательным и должно будет содержать больше одного символа, а вот y CurrentlyBeingEditedExistingPersonData нет никаких обязательных свойств, потому что в процессе редактирования любое свойство может превращено в null или пустую строку.).
Вообще говоря, я бы обошелся и обычным обработчиком события Tap, но вроде как это не рекомендуется, если я использую паттерн MVVM:
Ключевой идеей паттерна является взаимодействие с моделью через ViewModel, то есть в данном случае использование событий визуальных компонентов и их обработчиков нежелательно.
А атрибута Command у ListView нет. На данный момент мне не известно никакого другого выхода из ситуации кроме как использование SelectedPerson. Но мне не нравится, что при однократном тапе на пункт списка он так и останется выбранным (из-за этого, мы не можем попасть на страницу редактирование данных одного и того же человека ещё раз, пока не побываем на странице просмотра и редактирования другого человека). Я думал, что _selectedPerson = null; решит эту проблему, но оказалось не так.
Мне и самому кажется нелогичным, что в сеттере мы затираем свойство, устанавливая его значение в null, но судя по всему, так делают (как в этом примере или этом примере).
Отсутствие скомпилированных привязок
Интегрированная среда разработки Rider подсвечивает мне, что не понимает, на что ссылается currentlyBeingEditedExistingPersonData.
Погуглив, узнал о скомпилированных привязках из официальной документации, понял, что их желательно применять в целях производительность, но как сослаться на класс PeopleListViewModel.CurrentlyBeingEditedExistingPersonData - не понял и экспериментально выяснить это пока не удалось.
Примечание: мои уровни навыков на момент задавания вопроса
Привожу эту информацию затем, что Вы поняли, что потребуется объяснить на пальцах, а что можно подробно не объяснять.
- В C# я новичок (задавал вопросы по нему и WPF несколько лет назад, но так сложились обстоятельства, что я на время должен был оставить нативную разработку и сконцентрироваться на веб-разработке)
- В отличие от C#, в Xamarin у меня нет опыта вообще
- Мой основной язык - TypeScript; широко использую ООП и понимаю его основы. Суммарный стаж в веб-разработке с использованием ООП - 6 лет.
Сейчас я нахожусь в ситуации, когда хотя идей создания конкретных нативных приложений с помощью Xamarin у меня хоть отбавляй, но разрыв между навыками, полученными из учебных примеров и потребными навыками для реализации моих идей огромный (это мягко сказано).
