5

以下是使用metro API和數據綁定(使用MVVM)在下拉列表中填充文件夾列表的示例實現。將異步結果分配給數據綁定屬性

View模型的構造函數使用SetFolders方法(專用異步),該方法調用等待方法fileService.GetFoldersAsync()來獲取文件夾列表。然後將文件夾列表分配給名爲「FoldersList」的屬性。 XAML使用此屬性使用數據綁定來填充下拉列表。

我不知道是否有更好的方法來設置FoldersList屬性,而無需像下面那樣在構造函數中設置它。我希望調用GetFilesAsync方法,並在發生實際數據綁定時(不在類init中)設置FilesList屬性值。由於屬性不支持async/await修飾符(據我所知),我正在努力實現一個合適的解決方案。任何想法不勝感激。

代碼如下。

視圖模型

public class FileViewModel : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 
    private readonly IFileService fileService; 

    public FileDataViewModel(IFileService fileService) 
    { 
     this.fileService = fileService; 
     SetFolders(); 
    } 

    private async void SetFolders() 
    { 
     FoldersList = await fileService.GetFoldersAsync(); 
    } 

    private IEnumerable<IStorageFolder> foldersList; 
    public IEnumerable<StorageFolder> FoldersList 
    { 
     get { return foldersList; } 
     private set 
     { 
      foldersList = value; 
      if (PropertyChanged != null) 
      { 
       PropertyChanged(this, new PropertyChangedEventArgs("FoldersList")); 
      } 
     } 
    } 
} 

IFileService和實施

public interface IFileService { 
    Task<IEnumerable<IStorageFolder>> GetFilesAsync(); 
    } 

public class FileService : IFileService 
{ 
    public async Task<IEnumerable<IStorageFolder>> GetFoldersAsync() 
    { 
     var folder = KnownFolders.DocumentsLibrary; 
     return await folder.GetFoldersAsync(); 
    } 
} 

回答

6

我會實現它作爲一個懶惰的屬性,並使用ObservableCollection<T>而非IEnumerable<T>。我們正在幾個項目中完成它,並且運行良好。這樣您可以保證只在需要時才加載數據。此外,如果您需要預取,您可以始終在構造函數或其他地方調用load方法。

作爲一個側面說明,我personnaly不會暴露IStorageFolder直接從我的ViewModels。

private async Task LoadData() 
{ 
    if(!IsLoading) 
    { 
    IsLoading = true; 
    Folders = new ObservableCollection<Folder>(await fileService.GetFolderAsync()); 

    } 
    IsLoading = false; 
} 

private ObservableCollection<Folder> _folders; 

public ObservableCollection<Folder> Folders 
{ 
    get 
    { 
    if(_folders == null) 
    { 
     LoadData();//Don't await... 
    } 
    return _folders; 

    } 
    private set 
    { 
    SetProperty(ref _folders,value); 
    } 

} 
private bool _isLoading; 
public bool IsLoading 
{ 
    get 
    { 
    return _isLoading; 
    } 
    private set 
    { 
    SetProperty(ref _isLoading,value); 
    } 
} 

注意,您可以使用IsLoading屬性顯示例如進度環。之後,可觀察的集合被加載,您將能夠刷新它而不重新創建它。 (_folders.Add,_folders.Remove,_folders.Clear ...)

+0

我看到此答案的問題。屬性Folders的getter不會等待LoadData,因爲它應該被阻止。但是在LoadData中有一個函數正在等待,所以控制權會返回給調用者/屬性。對我來說這意味着_folders不一定被初始化,因此Folders屬性也可以返回null。 – buckley

+0

是的,控制權將返回給吸氣劑。但這不是問題,因爲IsLoading屬性將設置爲true,表示加載正在進行。當fileService.GetFolderAsync()任務完成時,LoadData方法的流將按預期繼續。確實,在調用結束時,Folders屬性將返回null。但只要GetFolderAsync()任務完成,就會引發PropertyChanged事件。 – Eilistraee

+0

事實上,文件夾屬性可以返回true。它按預期工作:您不希望阻止該調用,因此數據的延遲初始化不會同步發生。如果您需要等待LoadData,請在公用文件夾之前直接等待它。否則,只要數據可用,就讓數據綁定更新,或者依賴PropertyChanged。 – Eilistraee