1

ボタンをクリックすると完了するまでに約 5 秒かかる関数があります。ボタンがクリックされた場合、ボタンクリックが処理されていることを示す何らかの通知を表示したいと思います。

<Button Click="OnButtonClick" Content="Process Input" />

<Border x:Name="NotificationBorder" Opacity="0" IsHitTestVisible="False" 
        Width="500" Height="100" Background="White">
    <TextBlock Text="Your input is being processed" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>

そして、ボタンクリックのコードビハインドで:

private void OnButtonClick(Object sender, RoutedEventArgs e)
{
    DoubleAnimation da = new DoubleAnimation
        {
            From = 5,
            To = 0,
            Duration = TimeSpan.FromSeconds(2.5),
        };

    // Making border visible in hopes that it's drawn before animation kicks in
    NotificationBorder.Opacity = 1;
    da.Completed += (o, args) => NotificationBorder.Opacity = 0;

    NotificationBorder.UpdateLayout(); //Doesn't do anything
    UpdateLayout(); // Doesn't do anything

    NotificationBorder.BeginAnimation(OpacityProperty, da);

    // Simulate calculationheavy functioncall
    Thread.Sleep(5000);
}

どういうわけかUpdateLayout()レンダリングが十分に速くないため、通知は 5 秒が経過した後にのみ表示されThread.Sleepます。

Dispatcher.Invoke((Action)(() => NotificationBorder.Opacity = 1), DispatcherPriority.Render);どちらも機能しません。

さらに、別のワーカー スレッドで実行することはできませんThread.Sleep。実際のアプリケーションでは、Dispatcher が所有するオブジェクトからデータを読み取り、UI の一部を (再) 構築する必要があります。

Thread.Sleep()が呼び出される前に表示する方法はありますか?

4

3 に答える 3

1

問題は、メイン スレッド (UI スレッド) での長時間のアクティビティによって UI がフリーズするため、アニメーションが表示されないことです。

別のスレッドで計算を行う必要があります。uiObject.Dispatcher.BeginInvoke または Invoke を使用できる Dispatcher 所有のオブジェクトにアクセスする必要があります。BackgroundWorker は、いくつかの長い計算を実行し、UI スレッドで BackgroundWorker によって自動的に発生するイベント Completed をサブスクライブする方法を支援します。

UI フリーズに問題がなければ、いくつかのプロパティを変更し、その他のものを Dispatcher.BeginInvoke(DispatcherPriority.Background,...) で再スケジュールします。

NotificationBorder.Opacity = 1; Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(()=>Thread.Sleep(5000)));

于 2014-01-15T21:26:11.560 に答える
1

UI を更新する場合は、長時間実行される操作を非同期に変更する必要があります。これは、UI の依存関係を、常に時間がかかるロジックやおそらく IO バウンド操作から適切に分離することを意味します。これを行わないと、実行時間の長い操作で発生する UI の更新は、完了するまで実際には UI に反映されません。

.NET 4.5 で実行している場合、これを行う方法として私が推奨するパターンは次のとおりです。

    public async Task DoComplexStuff()
    {
        //Grab values from UI objects that must be accessed on UI thread

        //Run some calculations in the background that don't depend on the UI
        var result = await Task.Run((Func<string>)DoComplexStuffInBackground);

        //Update UI based on results

        //Possibly do more stuff in the background, etc.
    }

    private string DoComplexStuffInBackground()
    {
        return "Stuff";
    }

これは、古いスタイルの BackgroundWorker アプローチからはやや「後退」していますが、よりクリーンなロジックになる傾向があることがわかりました。UI 更新のための明示的な Invokes を使用してバックグラウンド コードを記述する代わりに、Task.Run(). また、この方法で呼び出すことができるメソッドにロジックをリファクタリングする必要があるため、これにより、UI 中心のコードと基礎となるロジック コードが自然に分離される傾向があります。とにかく、それは長期的な保守性にとって良い考えです。

于 2014-01-15T21:42:08.833 に答える
0

したがって、両方のアクション (UI と計算) をディスパッチャにディスパッチすると、それらは 1 行で実行されます。

Dispatcher.BeginInvoke(do ui things);
Dispatcher.BeginInvoke(do your logic things);

ただし、計算をバックグラウンド スレッドに移動することをお勧めします。

于 2013-03-24T12:40:17.310 に答える