我的问题与我读过的其他一些问题相似,但我无法找到我具体问题的答案。
注意:在提问之前,我已经阅读了以下问题:
Windows 8.1 应用商店应用的列表视图数据绑定
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()
。
我的问题是,有没有办法在LoadPayees()
完成加载数据后更新 UI 线程? 我还在某处读到使用Task
对 UI 也没有好处。 我的静态方法FileService.ReadFromFile()
返回一个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:
下面是根据请求的属性、View
和OnNavigatedTo()
的代码。
视图模型的属性 --
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
是来自DataContext CurrentPayee的外部StackPanel
的孩子。在当前收款人上,您没有收款人。不应多次设置 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>();
}
}
切勿为除事件处理程序以外的方法编写异步 void。
编辑:
更改视图模型中的可观察集合。您不应该为列表设置公共资源库。
private readonly ObservableCollection<Payee> _payees = new ObservableCollection<Payee>();
public ObservableCollection<Payee> Payees
{
get { return _payees; }
}
比循环并将项添加到集合中。现在,视图已收到通知。
foreach (var item in payeesFromJson)
{
Payees.Add(item);
}