1

WPF でモーダル ダイアログを実行するために、この素​​晴らしいフレームワークを使用しています。

このフレームワークを使用して、ユーザーが最初のダイアログ内からボタンをクリックしたときに、別のモーダル ダイアログをオーバーレイするモーダル ダイアログを取得しようとしています。DemoAppfor this フレームワークには、最初に を使用して、次に別の_dialogmanagerポップアップを表示 する例があります。MessageDialog

からこれを行うコードはDemoApp次のようになります。

private void ShowLayeredDialog()
{
    _dialogManager
        .CreateMessageDialog("Wait 2 secs...", "I'm the 1st dialog", DialogMode.Ok)
        .Show();

    ThreadPool.QueueUserWorkItem(o =>
        {
            Thread.Sleep(2000);
            _dialogManager
                .CreateMessageDialog("Hello again...", "I'm the 2nd dialog", DialogMode.Ok)
                .Show();
        });
}

私は似たようなことをしようとしましたが、CreateMessageDialog へのメソッド呼び出しを使用する代わりにCreateCustomContentDialog()、オブジェクトを取り、その内容UIElementをモーダル ビューで表示する ( a を指定) を使用したいと思いました。

そのため、最初のモーダル ビューに移動するために既に を呼び出した_dialogManagerので、DemoApp コードと同様の手法を使用して、新しい CustomContentDialog を生成するボタンをそのビューに作成しました。

ThreadPool.QueueUserWorkItem(o => _dialogManager.CreateCustomContentDialog(new SpikePhaseView(), DialogMode.Ok).Show());

残念ながら、バニラの UserControl であるのコンストラクターで、「多くの UI コンポーネントがこれを必要とするため、呼び出し元のスレッドは STA でなければならない」という例外が発生します。SpikePhaseView()

したがって、このエラーをここここで調査した結果、ApartmentState(ApartmentState.STA) を次のように設定する 2 番目のリンクから、受け入れられていないが非常に支持されたソリューションを実装しました。

var test = new Thread(() => _dialogManager.CreateCustomContentDialog(new SpikePhaseView(), DialogMode.Ok).Show());
test.SetApartmentState(ApartmentState.STA);
test.Start();

しかし、WpfDialogManagment フレームワーク コードのどこかで、「別のスレッドが所有しているため、呼び出し元のスレッドはこのオブジェクトにアクセスできません。' このコード ブロックで:

public void SetCustomContent(object content)
{
    CustomContent.Content = content;
}

上記では、CustomContent (System.Windows.Controls.ContentControl) が SpikePhaseView オブジェクトに設定されています。

編集

DemoApp では、2 つのモーダル ダイアログを正常に (エラーなしで) 起動できます。どのスレッドがこの CustomContext オブジェクトのコンテンツを設定しているかについて、この競合が発生しないと、1 つの UserControl (ビュー) が別の UserControl (ビュー) を生成できないのはなぜですか?

ApartmentState を設定すると最初のエラーを回避できたようですが、これがすべて Dispatcher を使用することに帰着する場合、誰かが私の状況でそれを使用して 2 番目のモーダル ビューを起動する呼び出しを開始する方法の例を提供できますか?

ありがとう

4

1 に答える 1

2

本当に本当に必要な場合を除き、アプリケーションに複数の UI スレッドを持たせたくありません。どのスレッドがどのコントロールを所有しているかを常に考える必要がある場合、問題は非常に複雑になります。UI スレッドが 1 つだけで、それがすべてを所有している場合は、はるかに簡単です。

STA スレッドを作成する新しいスレッドを作成しようとするのではなく、2 番目のポップアップがメイン UI スレッドから作成/アクセス/表示されることを確認してください。

C# 5.0 を使用している場合、これは非常に簡単です。

//Consider changing the return type to Task
//if this method is not used as an event handler
private async void ShowLayeredDialog()
{
    _dialogManager
        .CreateMessageDialog("Wait 2 secs...", "I'm the 1st dialog", DialogMode.Ok)
        .Show();

    await Task.Delay(2000);

    _dialogManager
        .CreateMessageDialog("Hello again...", "I'm the 2nd dialog", DialogMode.Ok)
        .Show();
}

このawait呼び出しにより、メソッドの残りの部分 (2 番目のダイアログの作成) が最初のタスクの継続として実行され、キャプチャされた SynchronizationContext (この場合は UI スレッドを表す) で実行されることが保証されます。最終的な結果として、コードがバックグラウンド スレッドをブロックすることはなく、宣言スコープを 1 つにとどめることができ、パフォーマンスが向上し、エラーをより適切に処理し、入力を減らすことができます。

これを行う C# 4.0 の方法は、もう少しコードが長くなります。

private void ShowLayeredDialog()
{
    _dialogManager
        .CreateMessageDialog("Wait 2 secs...", "I'm the 1st dialog", DialogMode.Ok)
        .Show();

    Task.Delay(2000).ContinueWith(t =>
    {
        _dialogManager
            .CreateMessageDialog("Hello again...", "I'm the 2nd dialog", DialogMode.Ok)
            .Show();
    }, TaskScheduler.FromCurrentSynchronizationContext());
}

これは (多かれ少なかれ) コンパイラによって最初のスニペットが変換されるものです。

于 2013-06-06T17:08:59.743 に答える