C# x:UWP应用程序中异步/等待加载数据集的绑定数据未显示

C# x:UWP应用程序中异步/等待加载数据集的绑定数据未显示,c#,asynchronous,data-binding,uwp,C#,Asynchronous,Data Binding,Uwp,在我的项目中,我最近改变了在我的应用程序(UWP应用程序)中加载数据的方式,并在加载过程中破坏了数据绑定(以前显示在绑定列表中的内容现在显示为空),但我不完全理解原因。我在这里看到了很多讨论异步数据绑定的帖子,我认为我已经很好地遵循了所有的原则 我之前使用file.OpenRead()加载了一个json文件,但为了解决遇到的另一个问题,我将其改为使用StorageFile流。因为我现在使用的是await-StorageFile.CreateFileAsync(),所以我看到我的OnNavigat

在我的项目中,我最近改变了在我的应用程序(UWP应用程序)中加载数据的方式,并在加载过程中破坏了数据绑定(以前显示在绑定列表中的内容现在显示为空),但我不完全理解原因。我在这里看到了很多讨论异步数据绑定的帖子,我认为我已经很好地遵循了所有的原则

我之前使用
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谢谢。。。从长远来看,我可能需要双向绑定,但作为测试问题,我尝试了单向绑定