none
添付ビヘイビアとアセンブリの関係 RRS feed

  • 質問

  • 先日から添付ビヘイビアを使ってて落とし穴に嵌り、本日やっと解決したのですが、いささか腑に落ちない状況になっております。以下少し長くなりますがご容赦ください。


    ■発端


     環境:XP/SP3、VisualStudio 2008 SP1、.NET Framework 3.5 SP1

    現在 WindowsForm と WPF の相互運用によるシステムを構築しております。Form から Window を起動している訳ですが、添付ビヘイビアの存在を知り、かずきさんの記事 をもとに C# で実装しました。
    添付ビヘイビアを汎用的にするため、専用のクラスライブラリを用意してそこに添付ビヘイビアを移動、Form から Window を起動すると、NullReferenceException が発生しました。

    ちなみにWindow の XAML は以下のとおりです。一部省略しています。 

    <Window x:Class="LoginWindow"
      xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Luna" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:presentation="clr-namespace:tenz.Tools.Presentation;assembly=tenz.Tools.Presentation" >
    ・・・・・・
      <Canvas>
    ・・・・・・
        <TextBox Canvas.Left="82" Canvas.Top="21" Height="23" 
             Name="TextCompanyCode" Width="184" TabIndex="1" 
             presentation:TextBoxBehaviors.IsFocusSelect="True"
             InputMethod.IsInputMethodEnabled="False" />
    ・・・・・・
      </Canvas>
    </Window>
    

    ■調査


     環境:XP/SP3、VisualStudio 2010、.NET Framework 4.0

    テストパターン1


     当初は相互運用が悪いのかと思って調べたものの原因がわからず、.NET Framework 3.5 の問題かもしれないと思い、以下のソリューションを組んで試験してみました。

    ・プロジェクトA (スタートプロジェクト) WindowsForms アプリケーション Form
    ・プロジェクトB  WPF クラスライブラリ  Window
    ・プロジェクトC  WPF クラスライブラリ  添付ビヘイビア

    プロジェクトA の Form から プロジェクトB の Window を起動。Window を起動しようとすると、今度は System.Windows.Markup.XamlParseException が発生しました。

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

    調べると XAML を定義すると自動生成される Window1.g.cs の以下のコードで例外を発生していることがわかりました。

    System.Windows.Application.LoadComponent(this, resourceLocater);
    


    テストパターン2


     相互運用だと 添付ビヘイビアは使えないのか確認しようと思い、以下の構成に変えてテストしてみました。


    ・プロジェクトA (スタートプロジェクト) WPF アプリケーション Window
    ・プロジェクトB  WPF クラスライブラリ  Window
    ・プロジェクトC  WPF クラスライブラリ  添付ビヘイビア


    プロジェクトA の Window から プロジェクトB の Window を起動。Window を起動しようとすると、またしても System.Windows.Markup.XamlParseException が発生しました。これはどうも相互運用が原因ではないようです。


    テストパターン3


     添付ビヘイビアと使用側の Window を別アセンブリにするとダメなのかと思い、以下の構成に変えたら今度は正常に起動しました。

    ・プロジェクトA (スタートプロジェクト) WPF アプリケーション Window
    ・プロジェクトB  WPF クラスライブラリ  Window ・ 添付ビヘイビア

     

    ■解決


     構成を変えているうちにハッと気づき、プロジェクト A の参照設定に プロジェクトC を加えると正常に動作するようになりました。下記いずれのプロジェクトの構成でも、参照設定に プロジェクトC を加えると動作しました。VS2008、2010 とも動作します。

    ・プロジェクトA (スタートプロジェクト) WindowsForms アプリケーション Form
    ・プロジェクトB  WPF クラスライブラリ  Window
    ・プロジェクトC  WPF クラスライブラリ  添付ビヘイビア

    ・プロジェクトA (スタートプロジェクト)  WPF アプリケーション Window
    ・プロジェクトB  WPF クラスライブラリ  Window
    ・プロジェクトC  WPF クラスライブラリ  添付ビヘイビア


    しかしプロジェクトA は直接添付ビヘイビア を使うわけじゃないのに、アセンブリをいちいち参照設定しなければならないのは、いささか面倒な話です。この辺りの仕組み、もしくは回避策をご存知の方はおられますでしょうか?


    ひらぽん http://d.hatena.ne.jp/hilapon/
    2010年9月30日 7:11
    モデレータ

回答

  • 実際にやってみました。

    obj\Debug フォルダにある Window2.g.cs を確認してもらえばわかりますが、
    Window2.InitializeComponent は Application . LoadComponent メソッド を使っています。
    Application はシングルトン パターンを実装し、そのウィンドウ、プロパティ、およびリソース スコープ サービスへの共有アクセスを提供します。

    なので、App.xaml の存在するアセンブリから参照可能ではないといけないようです。

     


    えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2010/12
    2010年10月6日 9:58

すべての返信

  • プロジェクトBの Window のパースを起動側であるプロジェクトAでやっているからだと思います。
    プロジェクトBに 非表示のダミー Window を仕込んでプロジェクトAはそれを起動して、非表示のダミー Window が本来起動したいWindowを起動したらどうでしょうか?

     


    えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2010/12
    2010年10月3日 10:15
  • えムナウさん、返信ありがとうございます。

    プロジェクトBの Window のパースを起動側であるプロジェクトAでやっているからだと思います。
    プロジェクトBに 非表示のダミー Window を仕込んでプロジェクトAはそれを起動して、非表示のダミー Window が本来起動したいWindowを起動したらどうでしょうか?

    試してみました。ソリューションは以下の構成です。

    XP/SP3・VS2010・.NET Framework 4.0

    プロジェクトA MainWindow
    プロジェクトB Window1  起動用のダミーWindow
                      Window2  添付ビヘイビアを設定
    プロジェクトC 添付ビヘイビア

    プロジェクトB に Window1 と Window2 の二つの Window を用意し、Window1  は起動用のダミーWindow、Window2 には添付ビヘイビアを仕込んであります。
    プロジェクトA の MainWindow の ButtonClick イベントで、Window1 の CreateWindow1 メソッドをコールすると・・・

    プロジェクトA・MainWindow

    namespace A {
    	public partial class MainWindow : Window {
    		public MainWindow() {
    			InitializeComponent();
    		}
    
    		private void button1_Click(object sender, RoutedEventArgs e) {
    			B.Window1 window = new B.Window1();
    			window.Visibility = System.Windows.Visibility.Hidden;
    			window.ShowDialog();
    			window.CreateWindow2();
    		}
    	}
    }
    

    プロジェクトB Window1

    namespace B {
    	public partial class Window1 : Window {
    		public Window1() {
    			InitializeComponent();
    		}
    
    		public Window2 CreateWindow2() {
    			Window2 window = new Window2();
    			window.ShowDialog();
    			return window;
    		}
    	}
    }
    

    プロジェクトB Window2

    <Window x:Class="B.Window2"
    		xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    		xmlns:test="clr-namespace:C;assembly=C"
    		Title="Window1" Height="150" Width="250">
    	<Grid>
    		<TextBox Height="23" HorizontalAlignment="Left" 
    				 Name="textBox1" VerticalAlignment="Top" Width="128" 
    				 Text="Hello World!"
    				 test:TextBoxBehaviors.IsFocusSelect="True"/>
    		<Button Content="Button" Name="button1" Height="23" Width="75" />
    	</Grid>
    </Window>
    

    添付ビヘイビアはかずきさんのを使わせて頂きました。

    結果・・・ダメでした。見事に System.Windows.Markup.XamlParseException が発生します。(´・ω・`)
    しかし、プロジェクトA の参照設定にプロジェクトC を追加すると正常に起動します。

    #書き込み時にレイアウトが崩れてしょうがない・・・

     


    ひらぽん http://d.hatena.ne.jp/hilapon/
    2010年10月4日 2:03
    モデレータ
  • >window.CreateWindow2();
    これをプロジェクトAでやってはダミーを作った意味がなくなります。
    プロジェクトBのダミーでやってください。

     


    えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2010/12
    2010年10月4日 3:37
  • >window.CreateWindow2();
    これをプロジェクトAでやってはダミーを作った意味がなくなります。
    プロジェクトBのダミーでやってください。

    返信ありがとうございます。

    B.Window2 の 生成を ダミーウィンドウ B.Window1 の Loaded イベントに実装してみました。
    結果・・・・・・ダメです。(-ω-;

    B.Window1 の Activated イベントでも同様にエラーでした。

    さらに試しに ダミーウィンドウ B.Window1 を表示するように変更し、Button を追加して B.Window1 の ButtonClick イベントで B.Window2 を起動しようとしても、見事に System.Windows.Markup.XamlParseException 例外が発生します。


    ひらぽん http://d.hatena.ne.jp/hilapon/
    2010年10月4日 5:24
    モデレータ
  • 実際にやってみました。

    obj\Debug フォルダにある Window2.g.cs を確認してもらえばわかりますが、
    Window2.InitializeComponent は Application . LoadComponent メソッド を使っています。
    Application はシングルトン パターンを実装し、そのウィンドウ、プロパティ、およびリソース スコープ サービスへの共有アクセスを提供します。

    なので、App.xaml の存在するアセンブリから参照可能ではないといけないようです。

     


    えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2010/12
    2010年10月6日 9:58
  • 実際にやってみました。

    obj\Debug フォルダにある Window2.g.cs を確認してもらえばわかりますが、
    Window2.InitializeComponent は Application . LoadComponent メソッド を使っています。
    Application はシングルトン パターンを実装し、そのウィンドウ、プロパティ、およびリソース スコープ サービスへの共有アクセスを提供します。

    なので、App.xaml の存在するアセンブリから参照可能ではないといけないようです。

     

    お忙しい中確認までして頂き、まことにありがとうございます。<(_ _)>

    Application はシングルトン パターンを実装し、そのウィンドウ、プロパティ、およびリソース スコープ サービスへの共有アクセスを提供します。

    この説明でやっと意味が判りました。実は最初の質問の時点で

    System.Windows.Application.LoadComponent(this, resourceLocater);

    で エラーを起こすとこまでは判っていたのですが、なるほどそういうことだったのですね! 

    なので、App.xaml の存在するアセンブリから参照可能ではないといけないようです。

    了解です。それは止むを得ませんね。(^ω^;
    今後は App.xaml の存在するアセンブリにも参照設定するようにします。有難うございました。


    ひらぽん http://d.hatena.ne.jp/hilapon/
    2010年10月6日 10:13
    モデレータ