3

いくつかのセクションを持つ既存の WPF アプリケーションがあります。すべてのセクションは、インターフェイスを実装する UserControl です。

void LoadData([...])インターフェイスはとの 2 つのメソッドを指定しますbool UnloadData()

これらのメソッドは UI スレッドによって呼び出されるため、時間がかかる場合は backgroundworker で作業を行う必要があります。

LoadDataUI を非同期で更新できるので問題ありません。問題は UnloadData() にあります。

現在のビューを本当に離れることができる場合、これは返されるはずです。

これは、データの現在のステータス (保存済み/変更済み/無効) で計算されます。

  • 保存されたリターンtrue、
  • Invalid は、正しいデータを保存するためにとどまるか、保存せずに終了するかを尋ねます
  • 変更をキャンセルする (true を返す) か、編集を続行する (false を返す) か、現在のデータを保存する (true を返す) ことができると変更されました。

問題は、「変更 -> 保存」にあります。これは時間のかかる方法であるため、アプリケーションの哲学を尊重するために、これをバックグラウンド スレッドで実行する必要があります (ビジー インジケーターを使用)。

しかし、スレッドを起動して次のセクションに進むと、メソッド呼び出しに「true」が返され、次のビューを直接起動します。

私の場合、ローカル データが保存される前に次のビューを読み込むと、問題が発生する可能性があります。

そう:

UI をブロックせずに、「true」を返す前にバックグラウンド スレッドが終了するのを待つ方法はありますか?

public bool UnloadData(){
   if(...){
      LaunchMyTimeConsumingMethodWithBackgroundWorker();
      return true;//Only when my time consuming method ends
   }
   //[...]
}

重要な編集 多分私は十分に明確ではありませんでした.BackgroundWorkerまたはTPLの使用方法を知っています. 私の問題は、親クラス (UnloadData() を呼び出すクラス) が編集できないクラスであることです (複数の理由で: リロードされない別の DLL にあり、すでに 70 以上の userControls で動作しており、すべて個別に動作します)。プロジェクト(dll)、リフレクションによってロードされます。

これは私の選択ではありません。良くないと思いますが、今は対処しなければなりません。私は主に、メソッドの戻り時にメソッドを待機させる方法を探しています。それが可能かどうかはわかりません。しかし、回避策を探しています。これにより、数週間の作業が不要になります。

4

6 に答える 6

5

さて、私は自分で何かを発見したかもしれないと思うので、興奮しています...

つまり、これは次のようになります。DispatcherFrameを作成し、そのフレームをDispatcherにプッシュし、RunWorkerCompletedで、Continue oftheFrameをfalseに設定します。

これまでのコードは次のとおりです。

public void Function()
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += TimeConsumingFunction;
    var frame = new DispatcherFrame();
    worker.RunWorkerCompleted += (sender, args) =>
                                     {
                                         frame.Continue = false;
                                     };
    worker.RunWorkerAsync();
    Dispatcher.PushFrame(frame);
}

private void TimeConsumingFunction(object sender, DoWorkEventArgs doWorkEventArgs)
{
    Console.WriteLine("Entering");
    for (int i = 0; i < 3; i++)
    {
        Thread.Sleep(1000);
    }
    Console.WriteLine("Exiting");
}

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    Function();
    Console.WriteLine("Returns");
}
于 2012-12-18T14:12:50.870 に答える
2

.NET4.5の新しい「待機」機能を試してみることができる場合があります。

awaitキーワードを使用すると、UIをブロックすることなく、 Taskオブジェクトの完了を待つことができます。

この変更を試してください:

public async bool UnloadData()
{
    if(...)
    {
        await Task.Factory.StartNew(() => 
            {
                LaunchMyTimeConsumingMethod();
            });
        return true;//Only when my time consuming method ends
    }
    //[...]
}
于 2012-12-18T15:07:50.723 に答える
2

ブール型の依存関係プロパティ「IsBusy」を実装する必要があります。これは、BackgoundWorkerを開始する前にTRUEに設定し、作業が完了するとFALSEに設定します。

UIでは、処理中に無効にしたい機能(次のビューをロードするためのボタンなど)をそのプロパティにバインドします。または、「キャンセル」ボタンを表示することもできます。

操作が完了するのを「待つ」べきではありません。BackgroundWorkerが設定する追加の変数で結果を取得できます。

    BackgroundWorker _bw;
    bool _returnValue = false;

    private void button_Click(object sender, RoutedEventArgs e)
    {  // if starting the processing by clicking a button
        _bw = new BackgroundWorker();

        IsBusy = true;

        _bw.DoWork += new DoWorkEventHandler(_bw_DoWork);

        _bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bw_RunWorkerCompleted);

        _bw.RunWorkerAsync();
    }

    void _bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        IsBusy = false;

        // retrieve the result of the operation in the _returnValue variable
    }


    void _bw_DoWork(object sender, DoWorkEventArgs e)
    {
        _returnValue = UnloadData();
    }

    private bool UnloadData()
    {
        if (...)
        {
            LaunchTimeConsumingMethod();
            return true;
        }
        else
            return false;

        //etc ...
    }

    public bool IsBusy
    {
        get { return (bool)GetValue(IsBusyProperty); }
        set { SetValue(IsBusyProperty, value); }
    }

    // Using a DependencyProperty as the backing store for IsBusy.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsBusyProperty =
        DependencyProperty.Register( ... )
于 2012-12-18T14:50:55.010 に答える
1

UnloadDataを非同期操作として扱い、非同期/待機機能が同期的に完了する場合と非同期で完了する必要がある場合の両方を処理できるようにします。

public async Task<bool> UnloadData(){
    if(...){
        // The await keyword will segment your method execution and post the continuation in the UI thread
        // The Task.Factory.StartNew will run the time consuming method in the ThreadPool
        await Task.Factory.StartNew(()=>LaunchMyTimeConsumingMethodWithBackgroundWorker());
        // The return statement is the continuation and will run in the UI thread after the consuming method is executed
        return true;
    }
    // If it came down this path, the execution is synchronous and is completely run in the UI thread       
    return false;
}


private async void button_Click(object sender, RoutedEventArgs e)
{
        // Put here your logic to prevent user interaction during the operation's execution.
        // Ex: this.mainPanel.IsEnabled = false;
        // Or: this.modalPanel.Visibility = Visible;
        // etc

        try
        {
            bool result = await this.UnloadData();
            // Do whatever with the result
        }
        finally
        {
            // Reenable the user interaction
            // Ex: this.mainPanel.IsEnabled = true;
        }
}

編集

UnloadDataを変更できない場合は、@ BTownTKDが指摘しているように、ThreadPoolで実行するだけです。

    private async void button_Click(object sender, RoutedEventArgs e)
{
        // Put here your logic to prevent user interaction during the operation's execution.
        // Ex: this.mainPanel.IsEnabled = false;
        // Or: this.modalPanel.Visibility = Visible;
        // etc

        try
        {

            // The await keyword will segment your method execution and post the continuation in the UI thread
            // The Task.Factory.StartNew will run the time consuming method in the ThreadPool, whether it takes the long or the short path
            bool result = await The Task.Factory.StartNew(()=>this.UnloadData());
            // Do whatever with the result
        }
        finally
        {
            // Reenable the user interaction
            // Ex: this.mainPanel.IsEnabled = true;
        }
}
于 2012-12-18T15:06:42.357 に答える
0

フレームワークのバージョンが4.0の場合は、おそらくTPLを使用する必要があります。

var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); // this will work only if you're running this code from UI thread, for example, by clicking a button
Task.Factory.StartNew(() => UnloadData()).ContinueWith(t => /*update ui using t.Result here*/, uiScheduler);

お役に立てれば。

于 2012-12-18T14:13:15.257 に答える
0

コールバック関数 (RunWorkerCompleted) を実装する必要があります。これは、バックグラウンド ワーカーが終了したときに呼び出されます。

ここで例を確認してください: http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx

于 2012-12-18T14:14:20.310 に答える