2

我创建了一个基于拆分页面示例应用程序的 Windows 8 Metro 应用程序。但是,在示例应用程序中,数据是在构造函数中同步加载的。我正在访问一个文本文件,因此需要异步加载数据。构造函数如下所示:

    public MyDataSource()
    {
        DataLoaded = false;

        LoadData();
    }

LoadData()是填充数据模型的异步方法。这工作正常,并按加载它的方式显示数据(这是我想要的行为)。当我尝试测试挂起和终止时会出现问题。问题是恢复有可能在填充数据模型之前尝试访问它:

    public static MyDataGroup GetGroup(string uniqueId)
    {
        // If the data hasn't been loaded yet then what?
        if (_myDataSource == null)
        {
        // Where app has been suspended and terminated there is no data available yet
        }

        // Simple linear search is acceptable for small data sets
        var matches = _myDataSource.AllGroups.Where((group) => group.UniqueId.Equals(uniqueId));
        if (matches.Count() == 1) return matches.First();
        return null;
    }

我可以通过将构造函数更改为 call 来解决此问题LoadData().Wait,但这意味着应用程序会锁定 UI 线程。我相信我需要的是一种让恢复代码GetGroup等待直到数据加载而不锁定 UI 线程的方法。这是可能的还是可取的,如果是的话,怎么做?

编辑:

一两个人建议将任务缓存为LoadData(). 这是一个好主意,但里面的代码GetGroup是由页面状态管理部分调用的,因此不能是异步的。为了解决这个问题,我尝试了以下方法:

if (!DataLoaded)
{
    //dataLoading = await MyDataSource.LoadData();
    dataLoading.RunSynchronously();
}

但这给了我一个错误:

RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method.

dataLoading.Wait()

只是锁定用户界面。

4

5 回答 5

5

我认为这听起来像是最好的选择,如果你制作了 constructor async。但既然这是不可能的,你可以做的是创建一个async工厂方法MyDataSource

private MyDataSource()
{
    DataLoaded = false;
}

public static async Task<MyDataSource> Create()
{
    var source = new MyDataSource();
    await source.LoadData();
    return source;
}

_myDataSource然后使用初始化await

_myDataSource = await MyDataSource.Create();

如果由于某种原因你不能这样做,你可以存储Task工厂方法返回的值并等待它GetGroup()

_myDataSourceTask = MyDataSource.Create();

…

public static async Task<MyDataGroup> GetGroup(string uniqueId)
{
    var myDataSource = await _myDataSourceTask;

    // Simple linear search is acceptable for small data sets
    var matches = myDataSource.AllGroups.Where(group => group.UniqueId == uniqueId);
    if (matches.Count() == 1) return matches.First();
    return null;
}
于 2012-07-18T19:51:30.253 回答
1

如果 LoadData 是异步的,则存储任何可等待对象(或创建一个新对象)并公开它(例如,像任务一样)然后 GetGroup 可以标记为异步并且可以执行 var data = await _myDataSource.LoadTask 或其他

于 2012-07-18T19:21:54.787 回答
1

我认为 Svick 最接近于回答这个问题,因为他使用了 Tasks。无论是在 GetGroup 方法上返回 Task 还是在 LoadAsync 方法上返回任务都可能无关紧要。重要的是您捕获该任务并稍后在简历中引用它。

Task 类在此处记录,您会注意到它具有IsCanceledIsCompletedIsFaulted等属性。当您的构造函数启动 LoadAsync 方法时,您可以将它返回的 Task 保存为类级变量。稍后,当您的恢复代码启动时,您可以检查任务是否仍在运行(即未完成且未出错)。如果任务已完成,您可以立即运行您的简历代码。如果尚未完成,您可以使用Task.ContinueWith安排您的简历代码在任务完成时运行。

于 2012-07-18T20:47:39.313 回答
0

我还没有检查 Windows 8,但我认为它的工作原理类似于 Windows Phone,假设您使用 Silverlight(或 WPF)来开发您的应用程序,我从您的问题中不清楚这一点。如果您使用 Silverlight:

您需要使用 INotifyPropertyChanged 接口和 ObservableCollection-s 才能将数据绑定到 UI。这样,每次您更改数据时,UI 都会收到有关更改的通知,并且绑定将刷新。

于 2012-07-18T17:48:59.773 回答
0

MyDataGroup 实现 iNotifyPropertyChanged 的​​公共属性。

UniqueID 一个公共属性。当 UniqueID 更改设置 MyDataGroup = null 然后调用 BackGroundWorker 到 var mathces = 但如果 LoadData(); 则延迟 正在工作中。由于这是在后台,延迟不会占用 UI。在回调设置 MyDataGroup 和 UI 将得到通知。确保您的后台工作人员是可取消的,因为您希望在 UniqueID 更改时取消它(如果它正在运行)。

我在 WPF 中执行此操作,因此如果它不能转换为 Metro 很抱歉,我将删除。

于 2012-07-18T20:29:35.727 回答