第一次初始化屏幕后如何运行代码?

时间:2015-01-14 16:39:00

标签: c# .net wpf caliburn.micro

该应用程序使用Caliburn.Micro及其屏幕向用户显示标签。

我知道在屏幕可见之前调用的IScreen.OnInitialize()方法。我希望有一个代码触发屏幕对用户完全可见 - 代表屏幕的选项卡应该在那里,但不一定要被选中。

我想在屏幕完全初始化并且可见之后触发数据加载(并且大部分代码本地运行在UI线程上而不进行编组)。

这就是现在的样子。

public class MyViewModel : 
    Conductor<IScreen>.Collection.OneActive,
    IHandleWithTask<DatabaseItemChangedMessage>
{
    // This method is called by EventAggregator
    public async Task Handle(DatabaseItemChangedMessage message)
    {
        await this.LoadData(true);
    }

    protected override void OnInitialize()
    {
        base.OnInitialize();

        Task.Factory.StartNew(() => this.LoadData(false).Wait()).ContinueWith(
            task =>
                {
                    if (task.Exception != null)
                    {
                        Execute.OnUIThread(
                            () =>
                            {
                                App.HandleException(task.Exception);
                                this.TryClose();
                            });
                    }
                });
    }

    protected async Task LoadData()
    {
        try
        {
            this.WorkStarted("Loading data...");

            TLoadedData loadedData;
            ISession transaction = null;
            // using (var transaction = this.DataItemRepository.BeginTransaction())
            // {
                 loadedData = await this.LoadDataInternal(transaction);
            //     transaction.Commit();
            // }

            Execute.OnUIThread(() => this.SetLoadedData(loadedData));
        }
        finally
        {
            this.WorkFinished();
        }
    }

    protected virtual void SetLoadedData(TLoadedData loadedData)
    {
        // Updates CollectionView's source using loadedData
    }

    protected virtual async Task<TLoadedData> LoadDataInternal(ISession session)
    {
        // loads data from database and returns them
    }

    protected virtual void WorkStarted(string description)
    {
        // Sets properties using to display progress
    }

    protected virtual void WorkFinished()
    {
        // Unsets properties using to display progress
    }
}

// Just an empty class for EventAggregator purposes
public class DatabaseItemChangedMessage
{
}

// Stores data loaded from the database
public class TLoadedData
{
}

1 个答案:

答案 0 :(得分:0)

在我看来,您的代码的主要问题是您正在创建一个全新的Task,以执行现有的异步操作。事实上,你应该做的只是启动该操作,等待它,并在必要时处理任何异常。

例如:

public class MyViewModel : 
    Conductor<IScreen>.Collection.OneActive,
    IHandleWithTask<DatabaseItemChangedMessage>
{
    // Ideally, async methods return a Task, but here you have to
    // match the base method signature, so you're stuck with void
    protected override async void OnInitialize()
    {
        base.OnInitialize();

        try
        {
            await this.LoadData(false);
        }
        catch (Exception e)
        {
            App.HandleException(e);
            this.TryClose();
        }    
    }

    // I added the "flag" parameter, so that it matched the calls from
    // your other code
    protected async Task LoadData(bool flag)
    {
        try
        {
            this.WorkStarted("Loading data...");

            TLoadedData loadedData;
            ISession transaction = null;
            // using (var transaction = this.DataItemRepository.BeginTransaction())
            // {
                 loadedData = await this.LoadDataInternal(transaction);
            //     transaction.Commit();
            // }

            // NOTE: by changing the caller of this method so that it's
            // initiated on the UI thread, now the continuations also run
            // on the UI thread, and you don't need to explicitly invoke them there.
            this.SetLoadedData(loadedData);
        }
        finally
        {
            this.WorkFinished();
        }
    }
}