2

GUIで長時間実行されるタスクを実行するために使用できる、作成したヘルパークラスがあります。スタイルを使用して「動作中の」アニメーションを表示し、コンテンツをフェードアウトして、タスクの実行中に何かが進行中であることをユーザーが確認できるようにします。

私の問題は、長時間実行されているタスクが完了すると、コンテンツがフェードインし、作業中のアニメーションが非表示になることです. GUI コンポーネントは、長時間実行されるタスクとは別に発生します。つまり、データ バインディングの OnPropertyChanged("") イベントが発生し、長時間実行されるタスクが完了した後で、これらのイベントが GUI スレッドによって取得されます。しかし、問題は、長時間実行されているタスクが完了するとワーカー アニメーションが閉じますが、データ バインディングが更新される前です。

したがって、最終的な結果として、タスクの実行中にワーカー アニメーションが期待どおりに表示されますが、データ バインディングの更新には 4 ~ 5 秒、またはすべてのツリー データの大規模なデータセットの場合はそれ以上の時間がかかり、この間、アプリケーションは動作しません。 「ワーキングアニメーションモード」でフリーズします。

長時間実行メソッドだけでなく、OnPropertyChanged からの関連付けられたデータ バインディングの更新に対しても、ワーカー アニメーションを実行し続ける方法はありますか?

4

2 に答える 2

0

答えてくれてありがとう。私は実際に、少し物議を醸す可能性のある解決策に出くわしましたが、それは少しハックだと解釈する人もいますが、それは私がやりたいことを正確に実行し、それを行う他の方法はないようです。私にとって、これはコード ソリューションであり、ハックではありません。

私は codeproject からダウンロードした WPFBackgroundProgressIndicator オープン ソース プロジェクトを使用しています (私が思うに)。これには、フェードアウトの有無にかかわらずメイン コンテンツにビジー インジケーターを表示するオプション、またはポップアップとして表示するオプションがあり、バックグラウンド スレッドとして実行されます。理想と選んだ理由。

問題は、長時間実行されるメソッドを実行すると、コードの実行は同期的に完了しますが、バインディングの OnPropertyChanged("") の更新はすべて非同期で実行され、Dispatcher スレッドでキューに入れられるため、WPF コントロールが呼び出す機会を得る前に作業メソッドが完了することです。新しい値を取得するための依存関係プロパティのゲッター。あなたがする必要があるのは、すべての Dispatcher イベントが完了するまで効果的に「ブロック」することです. データがまだレンダリングされている間にユーザーが視覚的に何かを実行できるようにしたくないので、完全な更新が完了するまでアプリケーションをブロックしたいので、それが私の要件です。クリーンなブロッキングは、乱雑な相互作用よりも望ましいです。

したがって、解決策は、信じられないかもしれませんが、作業メソッド呼び出しの直後にある 1 行のコードです。以下の通りです。

Application.Current.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null); 

ご覧のとおり、新しいタスクを Dispatcher スレッドで効果的にキューに入れ、現在のコードの実行をそれが終了するまでブロックしますが、最も低い優先度を与えると、この呼び出しはすべての OTHER ディスパッチャーの実行が終了するまで、つまりすべてのレンダリングが完了するまで待機します。レンダリングが完了すると、この行が実行され、すべてのレンダリングが完了して終了します。コンテキストで使用した完全な方法を以下に示します。このアプローチに関するあなたの考えと議論を歓迎します。

public void LongRunningTaskWithFade(BusyDecorator busy, Action longTask)
        {
            if (loading) return;
            loading = true;

            busy.FadeTime = TimeSpan.Zero;
            busy.IsBusyIndicatorShowing = true;

            // in order for setting the opacity to take effect, you have to delay the task slightly to ensure WPF has time to process the updated visual
            Application.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                try
                {
                    longTask();
                    Application.Current.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null); 
                }
                finally
                {
                    HideBusyDisplay(busy);                    
                }
            }), DispatcherPriority.Background);
        }
于 2012-11-16T10:02:44.797 に答える