将数据从 WCF 绑定到 MVVM 客户端始终为空
本文关键字:客户端 MVVM 数据 WCF 绑定 | 更新日期: 2023-09-27 17:50:47
我正在制作我的第一个Windows 8客户端。这也是我第一次使用异步方法来加载属性,所以如果这是一个菜鸟问题,请原谅我。
我有一个 WCF 服务,并从客户端的 Visual Studio 中的拆分页模板开始(但我几乎要替换所有内容(。
当我将数据直接放入我的视图时,后面的"SplitPage"代码后面的所有内容都会正确加载并显示出来。但是,当我尝试使用 MVVM 时,该属性在绑定时没有任何项。由于WCF是获取数据的异步调用,这是问题的原因吗?在返回属性之前,是否有确保他们的数据已返回(属性本身似乎无法标记为异步(?
这是我所做的:
出于测试目的,我放置了一个 ListView(代码显示列表框,但我在 XAML 中使用了列表视图(,并添加了以下代码作为在 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());
}
这工作正常,当页面加载时,用户列表框包含用户视图。
然后,我尝试进入完整的 MVVM 模式,并制作了一个 UsersViewModel 和 UsersView(复数(以及一个存储库,该存储库在其构造函数中初始化了一个属性 Users 与它从我的 WCF 服务中提取的 UserDTO 的可观察集合。这就是上述消息的最后一行所做的,就是将视图设置为页面上的堆栈面板。
视图和视图模型粘合在一个资源文件中:
<DataTemplate x:Key="vm:UsersViewModel">
<vw:UsersView />
</DataTemplate>
绑定与我习惯的绑定略有不同,因为显然 .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 的调用,因此在返回数据异步调用之前不会发生绑定,但由于某种原因使用 MVVM,数据绑定在后台线程上加载数据时会立即发生?如果是这样,当数据最终出现在属性中时,它是一个可观察集合的事实不应该得到 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
工厂方法的最大好处是永远不会获得尚未初始化的实例。
但是,在某些情况下,您必须使用公共构造函数而不是async
工厂方法,例如 IoC/DI 或数据绑定。
在这种情况下,我发现以下模式很有帮助:
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
介绍很有帮助。