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

时间:2020-02-03 06:42:25

标签: c# xamarin mvvm data-binding viewmodel

我正在尝试实现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,这里的“问题”部分是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”,更改的内容已更新,但不幸的是,它们未反映在UI中,希望您能帮助我找到我在做什么错误的。

谢谢!

3 个答案:

答案 0 :(得分:2)

您的BaseViewModel没有实现INotifyPropertyChanged接口。

答案 1 :(得分:1)

由于使用了MVVM,因此在列表视图中选择项目时就可以在ViewModel中直接处理逻辑(无需再定义ItemSelected事件)。

private ContactVm _selectedContact;

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

          SelectedContact.FirstName="Test";

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

并且不要忘记对模型和视图模型实现 INotifyPropertyChanged

答案 2 :(得分:0)

我猜想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";
}

唯一的区别是我也已经在ViewModel中实现了ObservableCollection的属性更改。

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

请注意,我没有使用您的SelectedContact绑定。可能就像您所说的那样,约束力才是问题。

我希望它能对您有所帮助。

相关问题