我正在尝试实现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中,希望您能帮助我找到我在做什么错误的。
谢谢!
答案 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绑定。可能就像您所说的那样,约束力才是问题。
我希望它能对您有所帮助。