2

このコードが機能しない理由を誰かが理解するのを手伝ってくれますか?

私がやりたいことは:ボタンをクリックし、ボタンの内容を変更してロックし、外部スレッドを実行し、最後にボタンのロックを解除して内容を再度変更します

private void button3_Click(object sender, RoutedEventArgs e)
{
    button3.Content = "Printing...";
    button3.IsEnabled = false;

    Thread.Sleep(1000);

    button3.IsEnabled = true;
    button3.Content = "Print";
}
4

4 に答える 4

2

この動作が発生する理由は、(ボタンのClickイベントを処理する) メソッドが UI と同じスレッドで実行されているためです。この概念は、UI コードを操作する際の問題の一般的な原因です。

簡単に言えば、メソッドが完了するまで UI を再描画することはできません。メソッド中にボタンを無効にし、1 秒待ってからボタンを再度有効にします。その後、UI が再描画され、ボタンが有効になります。このため、画面上で無効になっているボタンが表示されることはありません (この場合、アプリケーションは 1 秒間フリーズしますSleep)。

代わりに、バックグラウンド スレッドでタスクを実行するために使用できる方法の 1 つを調査する必要があります。メソッドは、このスレッドを作成して開始し、ボタンを無効に設定してから完了し、UI スレッドがボタンを無効として描画できるようにする必要があります。タスクが完了したら、ボタンを再度有効に設定する必要があります (通常、WPFでは、UI スレッドからDispatcher.Invoke確実に設定するために使用することを意味することに注意してください)。button.Enabled

System.Threadingバックグラウンド タスクを作成するには、名前空間 (具体的にはThread.Startメソッド)、またはTask Parallel Library、またはBackgroundWorkerクラスを調べることができます。TPL が最も簡単な出発点になると思います。

于 2013-11-04T11:08:46.357 に答える
2

UI スレッドをフリーズしているsleeping on UI threadため、UI の更新が表示されません。

コントロールの再描画は UI ディスパッチャーによって行われますが、ディスパッチャーはタスクに設定された優先度に基づいて操作を実行します。コントロールの再描画は、Render に設定されたディスパッチャの優先順位で行われます。ボタンのコンテンツの設定は、優先度のレンダリングでディスパッチャーのキューに入れられますが、優先度の高いすべてのタスクが完了するまで実行されません。

他の人が示唆しているように、実行時間の長いタスクを別のスレッドに移動する必要がありますが、UI スレッドでスリープする前に GUI を更新する別の回避策があります。前述のように、UI はすべてのタスクDispatcherPriority.Renderが完了すると再描画されます。だから、あなたができることは、次のタスクに移動before sleeping on thread enqueue an empty delegate with render priority synchronouslyする前にどれにするかです。force dispatcher to perform all tasks above and with priority renderこれは私が意味したものです -

private void button3_Click(object sender, RoutedEventArgs e)
{
    button3.Content = "Printing...";
    button3.IsEnabled = false;

    Dispatcher.Invoke((Action)(() => { }), DispatcherPriority.Render); <-- HERE
    // button3.Refresh(); <-- After having extension method on UIElement.
    Thread.Sleep(1000);

    button3.IsEnabled = true;
    button3.Content = "Print";
}

また、このメソッドを UIElement の拡張メソッドとして持つこともできます -

public static void Refresh(this UIElement uiElement)
{
    uiElement.Dispatcher.Invoke((Action)(() => { }), DispatcherPriority.Render);
}

button3.Refresh()そして、UI スレッドでスリープする直前に呼び出します。

ただし、ディスパッチャーは次のタスクに移動する前にレンダリング優先度以上のすべてのタスクを完了するため、button3 だけでなく、UI での更新が保留されている他のすべてのコントロールも更新されるため、これには欠点もあります。

ただし、常に心に留めておいてください。UI スレッドでスリープしないでください。

于 2013-11-04T11:49:34.990 に答える
-2

ボタンをクリックすると無効になりますが、再度有効にはなりません。あなたにできることは、

1. initiate a timer

when button is clicked then,
1. change content, disable the button
2. start the timer
3. after a certain duration when the external thread is executed enable the button again and change it's content.

これが役に立てば幸いです ありがとう。

于 2013-11-04T10:59:21.350 に答える