5

WPF アプリケーションの開発中に問題が発生しました。アプリケーションは Prism に基づいています。アプリケーションはプリズム ブートストラップを使用して起動し、ウィンドウをロードする前に、アプリは別のスレッド (STA) でモーダル ダイアログを開き、一連のもの (サービスなど) がロードされます。ダイアログはその間開いており、アプリケーションの起動プロセスの進行状況をユーザーに通知します (イベント アグリゲーターを使用して更新を渡します)。読み込みが完了すると、ブートストラップはダイアログを閉じ、メイン アプリケーション ウィンドウを開きます。 ここまでは順調ですね... その後、アプリケーションを閉じると、同じことが起こっています。メイン ウィンドウが閉じられ、ダイアログ ボックスが (再び新しい STA スレッドで) 開かれ、通知が許可されます。ただし、ShowDialog 呼び出し (新しい STA スレッド内で発生) をヒットすると、例外が発生します: 「親の Freezable とは異なるスレッドに属する DependencyObject は使用できません」。長時間のデバッグの後、例外の原因はウィンドウの背景であることがわかりました。これは、アプリケーションレベルでマージされた辞書から取得されたブラシ/画像です (wpf UI スレッドでインスタンス化されます)。ResouceDictionary なしで画像を読み込む場合 - すべてうまくいきます。


要約: 例外は、resourceDictionary を使用している場合にのみ、新しい STA スレッドへの 2 回目の呼び出しでのみ観察されます。これは、DialogBox をロードし、ShowDialog を呼び出したときに正確に例外を発生させます。ダイアログが 1 つしかない場合 (たとえば、ブート時とシャットダウン プロセスでのダイアログのみ)、例外は発生しません。


私の質問は次のとおりです。その理由は何ですか?この場合、この例外は正確にはどういう意味ですか? (一般に、他のスレッドから何らかの種類の UI スレッドの更新があることは理解していますが、なぜこれがダイヤルゴ + スレッドの 2 番目のインスタンスでのみ発生するのかわかりません)。

ありがとう :)

4

2 に答える 2

2

正しく述べたように、バックグラウンド オブジェクトはメイン UI スレッドで作成されました。背景は実際には Brush オブジェクトであり、Brush は DependencyObject です。

DependencyObject が作成されると、作成された STA スレッドに「依存」します。したがって、他の依存オブジェクトと同様に、独自のスレッドでのみ使用できます。これは、STA と古い COM オブジェクト モデルとの一種の互換性を意味します。

したがって、他の STA スレッドで使用しようとすると、適切な例外が発生します。

PSリソースとして定義された画像にも同じ問題があります。

于 2014-10-13T15:25:23.430 に答える
1

これについても同様の問題がありました。バックグラウンドをどのように実装しているのかわかりません。私は自分の状況を説明しようと試みることができます。独自のベース Window を作成しました。これを、Window から継承している MyWindow と呼びましょう。すなわち。

public class MyWindow : Window
{
}

私が目指していたのは、アプリケーション リソース ディクショナリの動的リソースから背景を適用することです。

私は最初にこの答えに行きました

public class MyWindow : Window
    {
        public MyWindow()
        {
            this.SetResourceReference(BackgroundProperty, "MyResourceKey");
        }
    }

リソースが設定された色である場合、これは私にとってはうまくいきました。

<SolidColorBrush x:Key="MyResourceKey" Color="White"/>

リソース参照をシステム カラーに設定すると、問題が発生することがわかりました。

<SolidColorBrush x:Key="MyResourceKey"  Color="{DynamicResource {x:Static SystemColors.WindowColorKey}}"/>    

初めては機能しますが、2回目には親のフリーズ可能なエラーが発生します。だから私の最初の考えは、ディスパッチャーを呼び出す必要があるだけのスレッドの問題だということでした。ウィンドウで呼び出す必要があると思ったので、ここで問題が発生しました。違います。そのリソースの依存オブジェクトでそれを呼び出す必要があります。
問題。リソースを探してそれへの参照を作成するため、SetResourceReference を使用するオブジェクトはありません。したがって、必要なのは実際の依存オブジェクトです。リソースからオブジェクトを取得するには、これを行うことができます。

object temp = this.TryFindResource("MyResourceKey");

これでオブジェクトができましたが、これは依存オブジェクトである必要があります。私はこれを試していませんが、これを行うことができるかもしれません

DependencyObject temp = (DependencyObjet)this.TryFindResource("MyResourceKey"); 

これで、依存オブジェクトができました! これが、フリーズ可能な親でスレッド化の問題を引き起こしている原因です。次に、このオブジェクトでディスパッチャーを呼び出します。私は最終的にこのようなものになりました。これは私にとってはうまくいきましたが、少しきれいにしようとするかもしれません。

public class MyWindow: Window
    {
        public MyWindow()
        {
            SetResources();                
        }

        private void SetResources()
        {
            DependencyObject dependencyObject;
            object temp;

                temp = this.TryFindResource("MyResourceKey");

                if (temp != null)
                {
                    if (temp is DependencyObject)
                    {
                        dependencyObject = (DependencyObject)temp;
                        if (!dependencyObject.CheckAccess())
                        {
                            dependencyObject.Dispatcher.BeginInvoke(new System.Action(() => { this.SetResources(); }));
                        }
                        else
                        {
                            this.SetValue(BackgroundProperty, temp);                            
                        }
                    }                   
                }
          }                  
    }

これで、background プロパティを設定するだけです。これはスタイルでも同じように機能するはずです。だからあなたはすることができます

this.SetValue(StyleProperty, temp)

それを理解するのに少し時間がかかりました。しかし、それが機能するようになると、私は爽快になりました。リソースが使用している依存オブジェクトでスレッドの問題が発生しているようです。最初は正しいスレッドですが、途中で別のスレッドが開始されます。まだこれを理解する必要があります。誰かがこれに対するより良い解決策を持っているなら、私はそれを見たいです.

于 2012-10-20T19:59:59.197 に答える