Изменения в свойстве, которое реализует INotifyPropertyChanged, не отражаются в пользовательском интерфейсе - PullRequest
0 голосов
/ 03 февраля 2020

Я пытаюсь реализовать шаблон MVVM, но, к сожалению, это занимает больше времени, чем ожидалось.

У меня ListView, заполненный ObservableCollection of ContactsVm, Добавление или удаление контактов работает отлично, проблема возникает при попытке изменить только один элемент из этой коллекции, выбрав его.

Xaml, где я устанавливаю свои привязки:

<ListView ItemsSource="{Binding ContactsToDisplay}" 
                  SelectedItem="{Binding SelectedContact, Mode=TwoWay}"
                  SeparatorColor="Black" 
                  ItemSelected="OnItemSelected">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextCell Text="{Binding FirstName}" 
                              Detail="{Binding Id}">
                        <TextCell.ContextActions>
                            <MenuItem 
                                Text="Delete" 
                                IsDestructive="true" 
                                Clicked="Delete_OnClicked"
                                CommandParameter="{Binding .}" />
                        </TextCell.ContextActions>
                    </TextCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView> 

Его cs:

public ContactBookApp()
        {
            InitializeComponent();
            MapperConfiguration config = new MapperConfiguration(cfg => {
                cfg.CreateMap<Contact, ContactVm>();
                cfg.CreateMap<ContactVm, Contact>();
            });

            BindingContext = new ContactBookViewModel(new ContactService(), new PageService(), new Mapper(config));

        }

        private void AddButton_OnClicked(object sender, EventArgs e)
        {
            (BindingContext as ContactBookViewModel)?.AddContact();
        }

        private void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
        {
            (BindingContext as ContactBookViewModel)?.SelectContact(e.SelectedItem  as ContactVm);
        }


        private void Delete_OnClicked(object sender, EventArgs e)
        {
            (BindingContext as ContactBookViewModel)?.DeleteContact((sender as MenuItem)?.CommandParameter as ContactVm);
        }
    }

Моя ViewModel, здесь часть «problemmati c» - это метод SelectContact, остальное я выкладываю на случай, если он поможет:

public class ContactBookViewModel : BaseViewModel
    {
        private readonly IContactService _contactService;
        private readonly IPageService _pageService;
        private readonly IMapper _mapper;
        private ContactVm _selectedContact;
        public ObservableCollection<ContactVm> ContactsToDisplay { get; set; }
        public ContactVm SelectedContact
        {
            get => _selectedContact;
            set => SetValue(ref _selectedContact, value);
        }
        public ContactBookViewModel(IContactService contactService, IPageService pageService, IMapper mapper)
        {
            _contactService = contactService;
            _pageService = pageService;
            _mapper = mapper;
            LoadContacts();
        }
        private void LoadContacts()
        {
            List<Contact> contactsFromService = _contactService.GetContacts();
            List<ContactVm> contactsToDisplay = _mapper.Map<List<Contact>, List<ContactVm>>(contactsFromService);
            ContactsToDisplay = new ObservableCollection<ContactVm>(contactsToDisplay);

        }

        public void SelectContact(ContactVm contact)
        {
            if (contact == null)
                return;
            //None of this approaches works:
            //SelectedContact.FirstName = "Test";
            //contact.FirstName = "Test;
        }
    }
}

Мой класс ContactVm:

public class ContactVm : BaseViewModel
    {
        private string _firstName;

        public int Id { get; set; }

        public string FirstName
        {
            get => _firstName;
            set => SetValue(ref _firstName, value);
        }   
    }

BaseViewModel:

public class BaseViewModel
    {
        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        protected void SetValue<T>(ref T backingField, T value, [CallerMemberName]string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(backingField, value))
                return;
            backingField = value;
            OnPropertyChanged(propertyName);
        }
    }

Как видите, я пытаюсь обновить для каждого выбранного контакта настройку FirstName = "Test", измененные обновляются, но, к сожалению, они не отражаются в пользовательском интерфейсе, надеюсь, вы поможете мне найти что я делаю не так.

Заранее спасибо!

Ответы [ 3 ]

2 голосов
/ 03 февраля 2020

Ваш BaseViewModel не реализует интерфейс INotifyPropertyChanged.

1 голос
/ 03 февраля 2020

Поскольку вы использовали MVVM, вы можете напрямую обрабатывать логи c в своей ViewModel, когда выбираете элемент в просмотре списка (вам больше не нужно определять событие ItemSelected).

private ContactVm _selectedContact;

public ContactVm SelectedContact
{
   set
   {
      if (_selectedContact!= value)
      {
          _selectedContact= value;

          SelectedContact.FirstName="Test";

          NotifyPropertyChanged("SelectedContact");
      }
   }
   get { return _selectedContact; }
}

И не забудьте реализовать INotifyPropertyChanged для вашей модели и модели представления.

0 голосов
/ 03 февраля 2020

Я думаю, атрибут NotifyPropertyChangedInvocator неправильно уведомляет об изменениях свойств. Но я не уверен в этом. Потому что ваш BaseViewModel не реализует интерфейс INotifyPropertyChanged.

Код ниже работает хорошо для меня. Вот как я использую это во всем моем проекте.

Я непосредственно извлек интерфейс INotifyPropertyChanged в моем BaseModel и реализовал изменения свойств.

public class BaseModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void NotifyPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

public class ContactVm : BaseModel
{
    private string _firstName;

    public int Id { get; set; }

    public string FirstName
    {
        get { return _firstName; }
        set
        {
            this._firstName = value;
            NotifyPropertyChanged();
        }
    }
}

Это то, что у меня есть в моем обратном вызове.

public void SelectContact(ContactVm contact)
{
    if (contact == null)
        return;
    contact.FirstName = "Test";
}

Единственное отличие состоит в том, что я реализовал изменения свойств для ObservableCollection в ViewModel.

public ObservableCollection<ContactVm> ContactsToDisplay 
{
    get { return _contactsToDisplay; }
    set
    {
        this._contactsToDisplay = value;
        NotifyPropertyChanged();
    }
}

Обратите внимание, что я не использовал вашу привязку SelectedContact в моем случае. Может быть, как вы сказали, что обязательным будет вопрос.

Надеюсь, это поможет вам.

...