2016-06-12 21 views
1

簡単に言えば、私は何がUIをブロックしているのか、それを修正する方法を見ることはできません。私はこれを何時間も見つめてきました。最初から アプリケーション起動時にバックグラウンドスレッドで非同期プロシージャを実行する方法

App.xaml

<Application x:Class="Front_Office.App" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      > 

    <Application.Resources> 
     ...... 
    </Application.Resources> 
</Application> 

App.xaml.cs

using Front_Office.ViewModels; 
using System.Windows; 

namespace Front_Office 
{ 
    /// <summary> 
    /// Interaction logic for App.xaml 
    /// </summary> 
    public partial class App : Application 
    { 
     protected async override void OnStartup(StartupEventArgs e) 
     { 
      base.OnStartup(e); 
      var mainWindow = new MainWindow { DataContext = await MainWindowViewModel.Create() }; 
      mainWindow.Show(); 
     } 
    } 
} 

MainWindow.xaml.cs

using System.Windows; 

namespace Front_Office 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 
    } 
} 

Factoryパターンを使用MainWindowViewMを作成する非同期呼び出しを伴うodel

public static async Task<MainWindowViewModel> Create() 
{ 
    var myClass = new MainWindowViewModel(); 
    await myClass.Initialize(); 
    return myClass; 
} 

private async Task Initialize() 
{ 
    PatientList = await GetPatientList(); 
    FilteredPatientList = PatientList; 
} 

private MainWindowViewModel() : base() 
{ 
} 

GetPatientList()

private Task<ObservableCollection<ViewPatient>> GetPatientList() 
     { 
      IsLoading = true; 

      var x = Task.Run(async() => 
      { 
       Thread.Sleep(30000); // SIMULATING A VERY LONG CALL. 
       return new ObservableCollection<ViewPatient>(await MedicalClient.GetAllPatientsAsync()); 
      }); 

      IsLoading = false; 
      return x; 
     } 

私は、アプリケーションの起動時にUIをブロックしないようにバックグラウンドスレッドでMedicalClient.GetAllPatientsAsync()Thread.Sleep()を実行しようとしています。運がない。

私は間違っていますか?

どのようなヘルプも大歓迎です。

編集#1:

次のように変更することで、私が必要として動作しているようです。これを行う正しい方法ですか? (それはここに記載されているように工場パターンを破るように思われる)。

/// <summary> 
/// Interaction logic for App.xaml 
/// </summary> 
public partial class App : Application 
{ 
    // Droped the async and the await 
    protected override void OnStartup(StartupEventArgs e) 
    { 
     base.OnStartup(e); 
     var mainWindow = new MainWindow { DataContext = MainWindowViewModel.Create() }; 
     mainWindow.Show(); 
    } 
} 

// Dropped async and await. Added Task.Run() 
public static MainWindowViewModel Create() 
{ 
     var myClass = new MainWindowViewModel(); // Create/Load ViewModel 
     Task.Run(()=> myClass.Initialize());  // Update ViewModel from Asynchronous methods. 
     return myClass; 
} 
+0

完了したときのデータで埋め?そのコードは起動時に実行されますが、UIはブロックされていますか?または、スタートアップ時にタスクが実行されていませんか? –

+0

@FernandoRodriguez Taskは実行中ですが、Task.Run(...)の実行中はUIがブロックされています。 –

答えて

0

私はほとんどASPの土地で働いています - 私はWPFプロジェクトを本当に素早くスピンアップしています。私は問題があなたが実際に同期的に呼び出されているMainViewの "初期化"メソッドを使用していると思っています。

私の専門用語が間違っている場合は、少なくともInitialize()を完了してからビューをレンダリングする必要があります。ビーイングは、あなたのコード自体がIntitialize()ので、私はあることを仮定している待ってCreate()を呼び出すアプリケーションの起動時に、次にある

var mainWindow = new MainWindow { DataContext = await MainWindowViewModel.Create() };

問題:私はあなたが次の行を持っていた見たところ私は、OnStartupクラスのように使用することをお勧めしようとしていました私が間違っていない限り、起動時にアプリケーションがロックされている場所です。あなたはいくつかのブレークポイントを投げて確認できますか?

私の提案は以下のようになりますが、リファクタリングが必要になるのは、オブジェクトが実際に表示するデータに依存するからです。

protected async override void OnStartup(StartupEventArgs e) 
    { 
     base.OnStartup(e); 
     var mainData = await GetPatientList(); 
     // Your MainWindowViewModel's Create() call would have to 
     //undo it's dependency on GetPatientList(), however. I don't really 
     //have a coherent example 
     var mainWindow = new MainWindow { DataContext = await MainWindowViewModel.Create() }; 
     mainWindow.Show(); 
    } 
+0

申し訳ありません...私はどのようにこれを達成するためにMainWindowViewModelからGetPatientList()を移動するのか分かりません。また、完全なコードでは、GetPatientList()はサーバーの異なる非同期プロシージャのうちの1つです。 (コードはSOの例では最小限に抑えられていました)。ありがとう。 –

+0

私の上記を参照してください編集。 –

+0

@AlanWayne私は100%ベストプラクティスを約束しませんでした!私はちょうどロジックがちょっと混乱していることに気づいた。私たちは非同期でキーワードを待っていますが、コード内の既存の場所とは別に実行して実行する必要がある場合は、Task.Run()メソッドを使用しました。誰かが私の答えを "ベストプラクティス"に編集してくれることを願っていました。うーん、今のところうまく働いてくれてうれしいよ:) –

2

あなたのケースでは、UIスレッドはブロックされていません。表示されることはありません。ここで
があなたの問題である:

public partial class App : Application 
{ 
    protected async override void OnStartup(StartupEventArgs e) 
    { 
     base.OnStartup(e); 
     var mainWindow = new MainWindow 
      { DataContext = await MainWindowViewModel.Create() }; 

     //next line will be executed after "Create" method will complete 

     mainWindow.Show(); 
    } 
} 

awaitキーワードは、現在のメソッドの実行を停止します(呼び出し元メソッドの実行を継続するか、次のイベントを継続...)、タスクが完了した場合にのみ、引き続き現在のスレッドを解放しました。
mainWindowはまだ初期化されていませんので、アプリケーションに何も表示されません。

は、私はあなたがメインウィンドウ内MainWindow.DataContextの初期化を移動させることができると思います(たとえばLoad EventHandlerのため)、その後、ウィンドウはすぐにあったであろうとCreate方法は何が起こっている

+0

私は混乱しています。コンストラクタで非同期呼び出しを使用してクラスを作成するためのファクトリパターンを理解しているので、MainWindowViewModelのコンストラクタはprivateであり、MainWindow.xaml.csからはアクセスできません。これは正しいです? –

+0

私の上記を参照してください編集。 –

+1

@AlanWayne:ビューモデルに非同期のファクトリパターンを使用することはできません。何らかの「ロード」状態でそれを作成し、初期化が完了したらVMプロパティを更新*する必要があります。 –

関連する問題