2015-01-14 1 views
-1

Приложение использует Caliburn.Micro и его экраны для отображения вкладок для пользователя.Как запустить код после инициализации экрана в первый раз?

Мне известен метод IScreen.OnInitialize(), который вызывается перед тем, как экран становится видимым. Я хотел бы, чтобы код запускался, экран полностью виден пользователю - вкладка, представляющая экран, должна быть там, но необязательно должна быть обязательно выбрана.

Я хотел бы запускать загрузку данных сразу после того, как экран полностью инициализирован и виден (и большая часть кода запускается в потоке пользовательского интерфейса изначально без маршалинга).

Как это выглядит сейчас.

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 
{ 
} 
+0

Вы синхронно ожидания на асинхронных методов. Не делай этого. 'ждут их. – Servy

+0

Кроме того, вам не нужно использовать 'Execute.OnUIThread' вообще в такой программе, если вы правильно используете TPL; из-за того, что продолжения будут ориентироваться на исходный контекст. – Servy

+0

1) Если я удаляю 'Execute.OnUIThread' в методе OnInitialize', вызывающий поток должен быть STA, потому что для этого требуется множество компонентов пользовательского интерфейса." бросается при создании экземпляра окна внутри метода «App.HandleException». – alik

ответ

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(); 
     } 
    } 
} 
+1

Я не мог ждать в OnInitialize, поскольку он блокирует показ экрана. Я предпочитаю сначала показывать экран, чем данные загрузки. Я не нашел точки входа для этого, поэтому я начинаю фоновое задание в фоновом потоке. – alik

+0

Оператор 'await' не блокируется, поэтому я понятия не имею, что вы подразумеваете под этим. –

+0

Метод OnInitialize не заканчивается до того, как LoadData выполняется с его работой. – alik

Смежные вопросы