将数据从WCF绑定到MVVM客户端始终为空

我正在制作我的第一个Windows 8客户端。 这也是我第一次使用异步方法加载属性所以如果这是一个noobie问题,请原谅我。

我有一个WCF服务,并从visual studio中的Split Page模板开始为客户端(但我将几乎取代所有东西)。

当我直接在“SplitPage”代码中将数据导入我的视图后,所有内容都正确加载并显示出来。 但是,当我尝试使用MVVM时,该属性在绑定时没有任何项目。 因为WCF它是一个异步调用来获取数据是问题的原因吗? 有没有确保他们的数据在返回属性之前回来了(属性本身似乎不能被标记为异步)?

这是我做的:

出于测试目的,我放了一个ListView(代码说明了listbox但是我在XAML中使用了listview)并添加了以下代码作为在OnNavigatedTo处理程序中调用的异步方法:

private async void GetUsersList() { ServiceClient client = new ServiceClient(); List _users = (await client.GetUsersAsync()).ToList(); foreach(UserDTO user in _users) { UserListBox.Items.Add(new UserView(user)); } TestStack.Children.Add(new UsersView()); } 

这工作正常,当页面加载时,UserListBox包含UserViews。

然后我尝试进入一个完整的MVVM模式并创建了一个UsersViewModel和UsersView(复数)以及一个存储库,该存储库在其构造函数中初始化一个属性User,其中包含从我的WCF服务中提取的UserDTO的ObservableCollection。 这就是上面消息的最后一行是将视图设置为页面上的堆栈面板。

视图和视图模型在资源文件中粘合在一起:

    

绑定与我习惯的有点不同,因为显然.Net 4.5不再具有DataTemplates上的x:Type属性。

存储库中加载数据的部分如下所示:

  private ObservableCollection _users = new ObservableCollection(); private ServiceClient _client = new ServiceClient(); public UserRepository() { GetUsers(); } public async void GetUsers() { var tempList = await _client.GetUsersAsync(); foreach(UserDTO item in tempList) { _users.Add(item); } } 

UsersViewModel的构造函数唯一做的就是创建存储库的实例,并将UserViewModel项加载到UserViewModel的observablecollection中:

 public UsersViewModel() { _repo = new UserRepository(); foreach (UserDTO item in _repo.Users) { _users.Add(new UserViewModel(item.Id)); } } 

我已经尝试将输出语句放在任何地方,并且确定“getter”属性返回一个空列表,即使直接在SplitPage代码中的相同代码返回我已经在数据库WCF中提供的测试项目。 它可以像代码运行的线程一样简单吗? 也许SplitPage在UI线程上运行对WCF的调用,因此在数据异步调用返回之前不会发生绑定但由于某种原因MVVM由于某种原因在数据加载到后台线程时立即发生数据绑定? 如果是这样的话,它不是ObservableCollection的事实,当数据最终出现在属性中时,会通知UI吗?

async方法在完成执行之前返回。 您没有看到任何用户,因为GetUsers返回到UserRepository构造函数,该构造函数在加载用户之前返回到UsersViewModel构造函数。

我最喜欢的解决方案是异步工厂方法,例如,对于UserRepository

 private UserRepository() { } private async Task InitializeAsync() { var tempList = await _client.GetUsersAsync(); foreach(UserDTO item in tempList) { _users.Add(item); } } public static async Task Create() { var ret = new UserRepository(); await ret.InitializeAsync(); return ret; } 

async工厂方法方法的最大好处是,您永远不会获得尚未初始化的实例。

但是,在某些情况下,您必须具有公共构造函数而不是async工厂方法,例如IoC / DI或数据绑定。

在这些情况下,我发现以下模式很有用:

 public MyConstructor() { Initialized = InitializeAsync(); } public Task Initialized { get; private set; } private async Task InitializeAsync() { // asynchronous initialization here } 

您可以将它应用于您的存储库,如下所示:

 private ObservableCollection _users = new ObservableCollection(); private ServiceClient _client = new ServiceClient(); public UserRepository() { Initialized = InitializeAsync(); } public Task Initialized { get; private set; } private async Task InitializeAsync() { var tempList = await _client.GetUsersAsync(); foreach(UserDTO item in tempList) { _users.Add(item); } } 

然后,您可以在依赖类中使用相同的模式:

 public UsersViewModel() { _repo = new UserRepository(); Initialized = InitializeAsync(); } public Task Initialized { get; private set; } private async Task InitializeAsync() { // Wait for the repository to initialize await _repo.Initialized; foreach (UserDTO item in _repo.Users) { _users.Add(new UserViewModel(item.Id)); } } 

作为旁注:作为一般规则,您应该避免async void 。 您可能会发现我的async / await介绍很有帮助。