WinRT 8.1 异步数据绑定未更新 UI

WinRT 8.1 Asynchronous DataBinding not updating UI

我的问题与我读过的其他一些问题类似,但我无法找到我的具体问题的答案。

注: 在提问之前我已经阅读了以下问题:

ListView Data Binding for Windows 8.1 Store Apps

WinRT ViewModel DataBind to async method

也就是说,我正在创建一个 Windows 8.1 应用程序,它异步加载一个文本文件,并将数据绑定到 ListBox。我确定该问题与非 UI 线程无法更新 UI 有关,因此即使我的数据源实现了 INotifyPropertyChanged,UI加载数据时未更新。这是我的 LoadPayees() 方法:

public async void LoadPayees()
{
    try
    {
        var json = await FileService.ReadFromFile("folder", "payees.json");
        IList<Payee> payeesFromJson = JsonConvert.DeserializeObject<List<Payee>>(json);
        var payees = new ObservableCollection<Payee>(payeesFromJson);
        _payees = payees;
    }
    catch (Exception)
    {     
        throw;
    }

    if (_payees == null)
    {
        _payees = new ObservableCollection<Payee>();
    }
}

LoadPayees() 在我页面的 OnNavigatedTo() 事件中被调用。我可以通过断点看到正在调用该方法,并且正在将收款人加载到 ObservableCollection<Payee> 中。 _payees 是一个 属性,它在设置时调用 OnPropertyChanged()

我的问题是,有没有办法让 UI 线程在 LoadPayees() 完成加载数据后更新?我还在某处读到,使用 Task 对 UI 也没有好处。我的静态方法 FileService.ReadFromFile() returns a Task<string>.

编辑:

这是我的方法 ReadFromFile(),它也调用 OpenFile():

public static async Task<string> ReadFromFile(string subFolderName, string fileName)
{
    SetupFolder();
    var file = await OpenFile(subFolderName, fileName);
    var fileContents = string.Empty;

    if (file != null)
    {
        fileContents = await FileIO.ReadTextAsync(file);
    }

    return fileContents;
}

public static async Task<StorageFile> OpenFile(string subFolderName, string fileName)
{
    if (_currentFolder != null)
    {
        var folder = await _currentFolder.CreateFolderAsync(subFolderName, CreationCollisionOption.OpenIfExists);
        return await folder.GetFileAsync(fileName);
    }
    else
    {
        return null;
    }
}

编辑 2:

这是请求的属性 ViewOnNavigatedTo() 的代码。

-- ViewModel 的属性--

private ObservableCollection<Payee> _payees;
private Payee _currentPayee;

public PayeesViewModel()
{
    _currentPayee = new Payee();
    _payees = new ObservableCollection<Payee>();
}

public ObservableCollection<Payee> Payees
{
    get { return _payees; }
    set
    {
        _payees = value;
        OnPropertyChanged();
    }
}

public Payee CurrentPayee
{
    get { return _currentPayee; }
    set
    {
        _currentPayee = value;
        OnPropertyChanged();
    }
}

-- 查看--

<StackPanel Orientation="Horizontal"
            DataContext="{Binding Path=CurrentPayee}"
            Grid.Row="1">
    <Grid>
        <!-- snip unrelated Grid code -->
    </Grid>
    <ListBox x:Name="PayeesListBox"
             Margin="50,0,50,0"
             Width="300"
             ItemsSource="{Binding Path=Payees}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock Text="{Binding Path=CompanyName}" />
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</StackPanel>

-- 后面的代码 --

private PayeesViewModel _vm = new PayeesViewModel();

public PayeesPage()
{
    this.InitializeComponent();
    this._navigationHelper = new NavigationHelper(this);
    this._navigationHelper.LoadState += navigationHelper_LoadState;
    this._navigationHelper.SaveState += navigationHelper_SaveState;

    DataContext = _vm;
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    _navigationHelper.OnNavigatedTo(e);
    _vm.LoadPayees();
}

我认为问题是您设置了两次 DataContext

<StackPanel Orientation="Horizontal"
            DataContext="{Binding Path=CurrentPayee}"
            Grid.Row="1">

DataContext = _vm;

ListBox 是来自外层 StackPanel 的 child 和 DataContext CurrentPayee。在 CurrentPayee 上,您没有收款人。您不应多次设置 DataContext。

顺便说一句。像下面这样更改您的代码:

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    _navigationHelper.OnNavigatedTo(e);
    await _vm.LoadPayees();
}

public async Task LoadPayees()
{
    try
    {
        var json = await FileService.ReadFromFile("folder", "payees.json");
        IList<Payee> payeesFromJson = JsonConvert.DeserializeObject<List<Payee>>(json);
        var payees = new ObservableCollection<Payee>(payeesFromJson);
        _payees = payees;
    }
    catch (Exception)
    {     
        throw;
    }

    if (_payees == null)
    {
        _payees = new ObservableCollection<Payee>();
    }
}

你永远不应该为事件处理程序之外的方法编写 async void。

编辑:

更改 ViewModel 中的 ObservableCollection。列表不应该有 public setter。

private readonly ObservableCollection<Payee> _payees = new ObservableCollection<Payee>();

public ObservableCollection<Payee> Payees
{
    get { return _payees; }
}

然后循环并将项目添加到集合中。现在,通知视图。

foreach (var item in payeesFromJson)
{
    Payees.Add(item);
}