WPFで、ライブラリにUserControlを定義して利用しようとしたところFileNotFoundExceptionが発生しました。
状況と対処法、並びに原因の考察についてご紹介致します。

状況

私が遭遇したFileNotFoundExceptionは次のような状況で発生しました。
  1. メインのアセンブリ (実行ファイルを出力) にメインウィンドウを定義
  2. コントロール類は、メインのアセンブリから参照しているライブラリ「Controls」に定義
  3. 一部コントロールで利用するコンポーネントをライブラリ「Vectors」へ定義し「Controls」から参照
  4. ビルド時のエラーは発生しない、実行時にFileNotFoundExceptionが発生してしまう
ちなみに例外発生時のソリューションと参照の構成は以下の通り。

イメージ 1
図1 ソリューションのプロジェクト構成

イメージ 2
図2 参照の構成

親アセンブリにメインウィンドウを定義し、子アセンブリにウィンドウに配置する各種コントロールを定義、更に孫アセンブリに子アセンブリのコントロールから利用する一部コンポーネントを定義したという形です。

イメージ 3
図3 例外

System.Windows.Markup.XamlParseException: 'ファイルまたはアセンブリ 'Sample.Controls.Vectors, PublicKeyToken=null'、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つかりません。'

内部例外
FileNotFoundException: ファイルまたはアセンブリ 'Sample.Controls.Vectors, PublicKeyToken=null'、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つかりません。

この状態のまま実行すると、図3のような例外が…。
WPF周りの.NET Framework標準アセンブリの参照が不足しているのか、ソリューションをクリーンしてリビルド掛けるべきなのか、いろいろ原因を考えやってみましたが、なかなか解決できませんでした。

問題の解決法

私が遭遇したFileNotFoundExceptionは、メインのアセンブリ (図2の「Main」) の参照に孫アセンブリ (子アセンブリから参照しているアセンブリ、図2で言うところの「Vectors」) を追加することで解決できました。

イメージ 4
図4 解決後の参照の構成

イメージ 5
図5 解決後の「メイン」からの参照

イメージ 6
図6 実行結果

結構、簡単な問題だったようですね。。

原因の考察

通常、メインの親アセンブリから参照している子アセンブリが、更に参照している孫アセンブリは、メインの親アセンブリをビルドする際に、ビルド結果の出力先に子アセンブリとともにコピーされます。
しかし、そのコピーが行われるのは、どうもコード上で本当に参照が必要とみなされた場合のみみたいなんですよね。
コード上で子アセンブリが孫アセンブリの中の定義を利用していると判断された際にコピーが実行されるようです。
しかし、XAML上だけで子アセンブリが孫アセンブリを利用した際は、子アセンブリが孫アセンブリを必要としているとみなされないようです。そのため、メインの親アセンブリの出力先ディレクトリに、親アセンブリから直接参照設定が行われていない孫アセンブリはコピーされないみたいなんです。

、、ここまでの原因の考察のお話は飽くまでもぼくの妄想の範囲内であり、実のところ細かい実験を行っていないのですが…