我正在制作我的第一个 Windows 8 客户端。这也是我第一次使用异步方法来加载属性,所以如果这是一个菜鸟问题,请原谅我。
我有一个 WCF 服务,并从 Visual Studio 中客户端的拆分页面模板开始(但我几乎要替换所有内容)。
当我将数据直接放入“SplitPage”代码中的 View 时,所有内容都已正确加载并显示出来。但是,当我尝试使用 MVVM 时,该属性在绑定(bind)时没有任何项目。由于WCF,它是获取数据的异步调用是问题的原因吗?是否可以在返回属性之前确保它们的数据返回(属性本身似乎无法标记为异步)?
这是我所做的:
出于测试目的,我放置了一个 ListView(代码显示为 listbox,但我在 XAML 中使用了 listview)并将以下代码添加为在 OnNavigatedTo 处理程序中调用的异步方法:
private async void GetUsersList()
{
ServiceClient client = new ServiceClient();
List<UserDTO> _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(复数)以及一个存储库,该存储库在其构造函数中使用从我的 WCF 服务中提取的 UserDTO 的 ObservableCollection 初始化属性 Users。这就是上述消息的最后一行所做的就是将 View 设置为页面上的堆栈面板。
View 和 View 模型在资源文件中粘合在一起:
<DataTemplate x:Key="vm:UsersViewModel">
<vw:UsersView />
</DataTemplate>
绑定(bind)与我习惯的有点不同,因为显然 .Net 4.5 不再具有 DataTemplates 上的 x:Type 属性。
加载数据的存储库部分如下所示:
private ObservableCollection<UserDTO> _users = new ObservableCollection<UserDTO>();
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 集合中:
public UsersViewModel()
{
_repo = new UserRepository();
foreach (UserDTO item in _repo.Users)
{
_users.Add(new UserViewModel(item.Id));
}
}
我已经尝试将输出语句放在任何地方,并且确实“getter”属性返回了一个空列表,即使直接在 SplitPage 代码中的相同代码返回了我已经在 WCF 提供的数据库中的测试项目。它可以像代码运行的线程一样简单吗?也许 SplitPage 正在 UI 线程上运行对 WCF 的调用,因此在数据异步调用返回之前不会发生绑定(bind),但是由于某种原因使用 MVVM 时,数据绑定(bind)会在后台线程上加载数据时立即发生?如果是这样,当数据最终确实出现在属性中时,它是 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<UserRepository> Create()
{
var ret = new UserRepository();
await ret.InitializeAsync();
return ret;
}
async
的最大好处工厂方法方法是您永远不会获得尚未初始化的实例。但是,在某些情况下,您必须使用公共(public)构造函数而不是
async
。工厂方法,例如 IoC/DI 或数据绑定(bind)。在这种情况下,我发现以下模式很有帮助:
public MyConstructor()
{
Initialized = InitializeAsync();
}
public Task Initialized { get; private set; }
private async Task InitializeAsync()
{
// asynchronous initialization here
}
您可以像这样将它应用到您的存储库:
private ObservableCollection<UserDTO> _users = new ObservableCollection<UserDTO>();
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
intro有帮助。
关于c# - 将数据从 WCF 绑定(bind)到 MVVM 客户端始终为空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14183258/