我有一个WinRT应用程序,里面有很多用户、项目、会议等。
我有一个主屏幕,主屏幕视图模型应该显示CurrentUser,并且有一个绑定到CurrentUser.ProjectList.的ListView
我使用UserProvider类在ViewModel中初始化CurrentUser,该类从数据库中获取所有必需的信息。
然后,我的问题变得非常类似:订阅嵌套(子)对象的INotifyPropertyChanged
我有一个用户和项目模型:
public class User
{
public int id { get; set; }
public string ForeName { get; set; }
public string Surname { get; set; }
... etc ...
public ObservableCollection<Project> ProjectList { get; set; }
public ObservableCollection<User> FriendList { get; set; }
... constructor
}
public class Project
{
public String Name { get; set; }
public int Id { get; set; }
public List<User> Users { get; set; }
public List<Meeting> Meetings { get; set; }
.. constructor ...
}
具有以下内容的视图模型:
class HomeScreenViewModel : INotifyPropertyChanged {
private User _currentUser;
public User CurrentUser
{
get { return this._currentUser; }
set
{
if (Equals(_currentUser, value)) return;
this._currentUser = value;
RaisePropertyChanged("CurrentUser");
}
}
//[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
我在这个视图模型中有一个方法,它可以获得当前用户
public async Task<bool> GetLoggedInUserAsync()
{
int testId = 0;
CurrentUser = await userProvider.GetCurrentUser(testId);
UserProjects = await userProvider.GetUsersProject(CurrentUser);
CurrentUser.ProjectList = UserProjects;
return true;
}
这在视图的loadState 中被调用
public MainPage()
{
this.InitializeComponent();
addMeeting = new AddMeetingFlyout();
_vm = new HomeScreenViewModel();
this.DataContext = _vm;
}
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
await _vm.GetLoggedInUserAsync()
}
我在XAML中的绑定,例如ProjectList和ForeName,如下所示:
<CollectionViewSource
x:Name="projectsViewSource"
Source="{Binding CurrentUser.ProjectList}"/>
...
<ListView
x:Name="projectList"
ItemsSource="{Binding Source={StaticResource projectsViewSource}}"
Grid.Row="1"
SelectionMode="None"
Style="{StaticResource DraggableListView}"
ScrollViewer.VerticalScrollBarVisibility="Visible"
IsItemClickEnabled="True"
>
<ListView.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource ProjectTileButton}" Content="{Binding Name}" Click="ProjectItem_Click" />
</DataTemplate>
</ListView.ItemTemplate>
<AddDeleteThemeTransition/>
</ListView>
...
<Button ...>
<TextBlock ...">
<Run Text="{Binding CurrentUser.ForeName}" />
</TextBlock>
</Button>
按钮内容CurrentUser.ForeName在视图模型中首次初始化CurrentUser时会触发INotifyPropertyChanged事件。这反映在视图中,但对CurrentUser.ForeName的任何进一步更改都不会引发任何后续的INotifyPropertyChanged事件。ProjectList也不会显示在视图中,并且不会激发INotifyPropertyChanged事件,即使我知道它在那里。
我花了很多天的时间研究实现INotifyPropertyChanged,以便对嵌套的子复杂对象(如CurrentUser.ProjectList)的更改能够传播到视图中。目前,发生这种情况的唯一方法是我强行打电话给
this._currentUser = value;
RaisePropertyChanged("CurrentUser");
我正在用一个按钮测试它,该按钮调用视图模型中名为MakeChange()的方法
public void MakeChange()
{
User updatedCurrentUser = CurrentUser;
CurrentUser = updatedCurrentUser;
}
这是有效的,所以我知道所有数据都正确地来自数据库,而且一切都是应该的——少了一件需要担心的事情!
然而,我根本无法获得在页面加载时或添加新项目时显示用户项目的视图。
我尝试实施此解决方案:https://gist.github.com/thojaw/705450然而,WinRT的反射能力已经发生了变化,我不确定如何在我的项目中使用以下留置权,因为这超出了我的能力范围:
//from property
//in _type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
//where _inotifyType.IsAssignableFrom(property.PropertyType)
//select property;
任何帮助都将不胜感激——我真的以为我所要做的就是将CurrentUser.ProjectList绑定到ListView。
在替换整个ObservableCollection本身时,还需要为集合属性引入另一个属性更改事件和支持字段。
这里有一个很好的例子