将数据从 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 通知吗?

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

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介绍很有帮助。