C# x:UWP应用程序中异步/等待加载数据集的绑定数据未显示
在我的项目中,我最近改变了在我的应用程序(UWP应用程序)中加载数据的方式,并在加载过程中破坏了数据绑定(以前显示在绑定列表中的内容现在显示为空),但我不完全理解原因。我在这里看到了很多讨论异步数据绑定的帖子,我认为我已经很好地遵循了所有的原则 我之前使用C# x:UWP应用程序中异步/等待加载数据集的绑定数据未显示,c#,asynchronous,data-binding,uwp,C#,Asynchronous,Data Binding,Uwp,在我的项目中,我最近改变了在我的应用程序(UWP应用程序)中加载数据的方式,并在加载过程中破坏了数据绑定(以前显示在绑定列表中的内容现在显示为空),但我不完全理解原因。我在这里看到了很多讨论异步数据绑定的帖子,我认为我已经很好地遵循了所有的原则 我之前使用file.OpenRead()加载了一个json文件,但为了解决遇到的另一个问题,我将其改为使用StorageFile流。因为我现在使用的是await-StorageFile.CreateFileAsync(),所以我看到我的OnNavigat
file.OpenRead()
加载了一个json文件,但为了解决遇到的另一个问题,我将其改为使用StorageFile
流。因为我现在使用的是await-StorageFile.CreateFileAsync()
,所以我看到我的OnNavigatedTo()
在对象创建完成之前正在运行下一行代码。因此,我尝试将其转换并使其等待async对象,这在计时方面效果很好,只是绑定不再显示数据(设置断点确实表明ObservableCollection有数据)
我想我把它归结为这里的相关代码片段:
MainPage.xaml:
<ListView x:Name="lstAudioFX" ItemsSource="{x:Bind AudioFXModel.FXList}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:CheckBoxData">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<CheckBox Content="{x:Bind Name}" IsChecked="{x:Bind Selected, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
AudioFXViewModel.cs
public class CheckBoxData
{
public string Name { get; set; }
public bool Selected { get; set; }
}
public class AudioFXViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(propertyName)));
}
private bool _fNeedsProcessing = false;
public bool NeedsProcessing { get => _fNeedsProcessing; }
private ObservableCollection<CheckBoxData> _FXList;
public ObservableCollection<CheckBoxData> FXList {
get => this._FXList;
set
{
_FXList = value;
this.OnPropertyChanged("FXList");
}
}
private AudioFXViewModel(){}
// Originally public static AudioFXViewModel Create()
public static async Task<AudioFXViewModel> CreateAsync()
{
AudioFXViewModel audioFXViewModel = new AudioFXViewModel();
// Originally audioFXViewModel.LoadFXContent();
await audioFXViewModel.LoadFXContentAsync();
return audioFXViewModel;
}
// Originally private void LoadFXContent()
private async Task LoadFXContentAsync()
{
// Originally using File.OpenText()
StorageFile jsonFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri($"ms-appx:///Data/Settings.json"));
using (StreamReader sr = new StreamReader(await jsonFile.OpenStreamForReadAsync()))
{
var json = sr.ReadToEnd();
// The relevant info here is that FXData has a serialized list (FXList) of applicable FX
var data = JsonConvert.DeserializeObject<FXData>(json);
ObservableCollection<CheckBoxData> FXs = new ObservableCollection<CheckBoxData>();
foreach (string s in data.FXList)
{
FXs.Add(new CheckBoxData() {Name = s, Selected = true });
}
FXList = new ObservableCollection<CheckBoxData>(FXs);
_fNeedsProcessing = true;
}
}
}
公共类CheckBoxData
{
公共字符串名称{get;set;}
已选择公共布尔值{get;set;}
}
公共类AudioFXViewModel:INotifyPropertyChanged
{
公共事件属性更改事件处理程序属性更改;
public void OnPropertyChanged([CallerMemberName]字符串propertyName=null)
{
this.PropertyChanged?.Invoke(this,newpropertychangedeventargs(nameof(propertyName));
}
私有布尔_fNeedsProcessing=false;
公共布尔需要处理{get=>\u fNeedsProcessing;}
私有可观察收集列表;
公共ObservableCollection FXList{
get=>这个;
设置
{
_FXList=值;
此.OnPropertyChanged(“FXList”);
}
}
私有AudioFXViewModel(){}
//最初的公共静态AudioFXViewModel创建()
公共静态异步任务CreateAsync()
{
AudioFXViewModel AudioFXViewModel=新的AudioFXViewModel();
//最初是audioFXViewModel.LoadFXContent();
等待audioFXViewModel.LoadFXContentAsync();
返回audioFXViewModel;
}
//最初是私有的void LoadFXContent()
专用异步任务LoadFXContentAsync()
{
//最初使用File.OpenText()
StorageFile jsonFile=等待StorageFile.GetFileFromApplicationUserAsync(新Uri($”ms-appx:///Data/Settings.json"));
使用(StreamReader sr=new StreamReader(wait jsonFile.OpenStreamForReadAsync())
{
var json=sr.ReadToEnd();
//此处的相关信息是FXData具有适用FX的序列化列表(FXList)
var data=JsonConvert.DeserializeObject(json);
ObservableCollection FXs=新的ObservableCollection();
foreach(data.FXList中的字符串s)
{
Add(new CheckBoxData(){Name=s,Selected=true});
}
FXList=新的ObservableCollection(FXs);
_fNeedsProcessing=true;
}
}
}
我以前在设置ObservableCollection时不需要调用OnPropertyChanged的,但即使这样似乎也没有什么区别。如果我从AudioFXModel=await AudioFXViewModel.CreateAsync()中删除await
代码>,则数据将绑定/显示良好,但我回到了我的原始版本,其中,需要处理
尚未设置,因为它没有等待
**更新**
因此,在这里进行一些额外的调试时,对GetFileFromApplicationUriAsync()
的调用似乎是导致场景中断的原因我同意上面的评论,即您应该在标记中设置Mode=one-way
属性:
ItemsSource="{x:Bind AudioFXModel.FXList, Mode=OneWay}"
由于调用的异步性质,您可能会遇到一个计时问题,其中x:Bind尝试绑定到尚不存在的属性,并且在创建该属性时可能不会收到通知。如果您的MainPage
也实现了inotifPropertyChanged
,并引发了AudioFXModel
的OnPropertyChanged
事件,则应该可以解决问题
因此,在MainPage.xaml.cs中添加类似的内容:
public AudioFXViewModel AudioFXModel;
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
// Originally was just AudioFXViewModel.Create(
AudioFXModel = await AudioFXViewModel.CreateAsync();
// Breakpoint here was initially hitting before Create() finished prior to modifying to await / async
if (AudioFXModel.NeedsProcessing)
{
...
}
base.OnNavigatedTo(e);
}
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private AudioFXViewModel _audioFXModel;
private AudioFXViewModel AudioFXModel
{
get
{
return _audioFXModel;
}
set
{
_audioFXModel = value;
OnPropertyChanged();
}
}
// REST OF YOUR CODE HERE
}
还要注意的是,在创建PropertyChangedEventArgs
的方式中,它们总是传递“propertyName”,而不是实际进行调用的属性的名称。您需要删除nameof()
,如上所示。我同意上面的意见,即您应该在标记中设置Mode=OneWay
属性:
ItemsSource="{x:Bind AudioFXModel.FXList, Mode=OneWay}"
由于调用的异步性质,您可能会遇到一个计时问题,其中x:Bind尝试绑定到尚不存在的属性,并且在创建该属性时可能不会收到通知。如果您的MainPage
也实现了inotifPropertyChanged
,并引发了AudioFXModel
的OnPropertyChanged
事件,则应该可以解决问题
因此,在MainPage.xaml.cs中添加类似的内容:
public AudioFXViewModel AudioFXModel;
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
// Originally was just AudioFXViewModel.Create(
AudioFXModel = await AudioFXViewModel.CreateAsync();
// Breakpoint here was initially hitting before Create() finished prior to modifying to await / async
if (AudioFXModel.NeedsProcessing)
{
...
}
base.OnNavigatedTo(e);
}
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private AudioFXViewModel _audioFXModel;
private AudioFXViewModel AudioFXModel
{
get
{
return _audioFXModel;
}
set
{
_audioFXModel = value;
OnPropertyChanged();
}
}
// REST OF YOUR CODE HERE
}
还要注意的是,在创建PropertyChangedEventArgs
的方式中,它们总是传递“propertyName”,而不是实际进行调用的属性的名称。您需要删除nameof()
,使其看起来像上面那样。只是一个猜测,但是如果您将base.OnNavigatedTo(e)
到重写方法的顶部。@Stefan-很遗憾,这没有什么区别。ItemsSource=“{x:Bind AudioFXModel.FXList}”
可能应该使用Mode=one-way
@quinmars谢谢。。。从长远来看,我可能需要双向绑定,但作为测试问题,我尝试了单向绑定