mandarine-wp7更新
以下更新しました。
ViewModelGenerate.tt, Util.tt
必要なusingキーワードを生成元クラスから取得して生成する処理を追加。
生成するViewModelのプロパティにusingが必要な型を指定できなかった不具合?を解消。
上記に伴い、全体的に構成を見直してリファクタリング。
これでusingが必要な大抵の型は自動生成のプロパティとして記述できるようになった。
解説
VideModelを生成する場合、元になるViewModel派生クラスに対してPropertyDecl属性を記述するのは以前に解説した通りだ。
-
- SettingsViewModel.cs
[PropertyDecl("UseGeoLocation", typeof(bool))] [PropertyDecl("UseCookie", typeof(bool))] public partial class SettingsViewModel {}
-
- 生成されるSettingsViewModel_Generated.cs
public partial class SettingsViewModel: Mandarine.MVVM.ViewModel.ViewModel { #region private bool _useGeoLocation ; public bool UseGeoLocation { get { return _useGeoLocation ; } set { if ( _useGeoLocation == value ) return; _useGeoLocation = value; NotifyPropertyChanged(() => UseGeoLocation); } } #endregion #region private bool _useCookie ; public bool UseCookie { get { return _useCookie ; } set { if ( _useCookie == value ) return; _useCookie = value; NotifyPropertyChanged(() => UseCookie); } } #endregion #region private List<Language> _selectLanguage ; public List<Language> SelectLanguage { get { return _selectLanguage ; } set { if ( _selectLanguage == value ) return; _selectLanguage = value; NotifyPropertyChanged(() => SelectLanguage); } } #endregion }
しかしこれが上手くいくのはプロパティの型にusingの必要無い型(string, object, int, etc..)だけであり、例えば以下のようにGeneric List型をプロパティとして属性に記述した場合、コードの生成自体は上手く行くが、usingが解決できないため、コンパイルできない。
-
- SettingsViewModel.cs
〜 [PropertyDecl("SelectLanguage", typeof(List<Language>))] public partial class SettingsViewModel {}
-
- 生成されたSettingsViewModel_Generatedはコンパイルが通らない
この問題を解決策はシンプルで生成するコードにusingを記述することだ。
最終的には生成元ソースコードのEnvDTE80.CodeImportインタフェースからネームスペースを取得してそこからusingを生成したが、これが結構大変だった。
EnvDTE、EnvDTE80ネームスペースで参照される型の実装は殆どはCOMオブジェクトであり、レイトバィンディングのお陰でランタイムになって初めてプロパティの実装があるか無いかが判る。
IEnumerable<EnvDTE80.CodeImport> usings = GetCodeImports(clazz); foreach (EnvDTE80.CodeImport import in usings) { GenerateUsingClause(import.FullName); }
foreach中のEnvDTE80.CodeImport#FullNameプロパティはコンパイル時にはエラーにならないが、実行すると内部メソッド get_FullNameが無いといってエラーになるのである。
エラー 2 変換を実行しています: System.Runtime.InteropServices.COMException (0x80004005): エラー HRESULT E_FAIL が COM コンポーネントの呼び出しから返されました。 場所 System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo) 場所 Microsoft.VisualStudio.CSharp.Services.Language.CodeModel.CCodeImport.get_FullName() 場所 EnvDTE80.CodeImport.get_FullName() 場所 Microsoft.VisualStudio.TextTemplating6F04A7FB144E1009D6592CA01BD356CE.GeneratedTextTransformation.GenerateClass(CodeClass clazz, IEnumerable`1 usings) 場所 e:\.Mandarine\Mandarine\CodeGen\ViewModelGenerate.tt:行 52 場所 Microsoft.VisualStudio.TextTemplating6F04A7FB144E1009D6592CA01BD356CE.GeneratedTextTransformation.TransformText() 場所 e:\.Mandarine\Mandarine\CodeGen\ViewModelGenerate.tt:行 11 場所 Microsoft.VisualStudio.TextTemplating.TransformationRunner.RunTransformation(TemplateProcessingSession session, String source, ITextTemplatingEngineHost host, String& result) 1 1
正解はFullNameではなくNamespaceを使うことなのだが、対象がオートメーションオブジェクトでありCOMであることを思い出すまではで延々と上記エラーを出す羽目になってしまった。