属性 中实现 INotify属性 的更改未反映在 UI 中

Changes in Property that implements INotifyPropertyChanged not being reflected in UI

我正在尝试实施 MVVM 模式,但遗憾的是花费的时间比预期的要长。

我有一个由 ContactsVm 的 ObservableCollection 填充的 ListView,添加或删除联系人效果很好,当试图通过选择它仅更改此集合中的一个项目时出现问题。

我设置绑定的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,这里的 "problematic" 部分是 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;
        }
    }
}

我的联系人class:

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",更改已更新,但不幸的是它们没有反映在 UI 中,希望您能帮我找出我做错了什么。

提前致谢!

我猜 NotifyPropertyChangedInvocator 属性没有正确通知 属性 更改。但我不确定。因为您的 BaseViewModel 没有实现 INotifyPropertyChanged 接口。

下面的代码对我来说工作正常。这就是我在整个项目中使用它的方式。

我直接在 BaseModel 中派生了 INotifyPropertyChanged 接口并实现了 属性 更改。

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";
}

唯一的区别是我也对 ViewModel 中的 ObservableCollection 进行了 属性 更改。

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

请注意,在我的案例中我没有使用您的 SelectedContact 绑定。可能正如您所说,绑定将是问题所在。

希望对您有所帮助。

由于您使用了 MVVM ,当您 select 列表视图中的项目时,您可以直接在 ViewModel 中处理逻辑(您不再需要定义 ItemSelected 事件)。

private ContactVm _selectedContact;

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

          SelectedContact.FirstName="Test";

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

并且不要忘记为您的模型和视图模型实现 INotifyPropertyChanged

您的 BaseViewModel 没有实现 INotifyPropertyChanged 接口。