使用 WPF 的 CodeDom - 运行时出错

CodeDom using WPF - error at runtime

我有一个项目,我使用 Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider

在内存中编译了很多文件

我开始尝试使用 wpf 时出现了问题 windows。

我能够编译内存中的程序集,但是当我打开 window 时,我得到:

System.Exception: The component 'Dynamic.DragonListForm' does not have a resource identified by the URI '/ScriptCode;component/wpf_ui/dragonlistform.xaml'. at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)

注意:我通过在特定文件夹中添加所有 .cs 文件的列表进行编译

objCompileResults = objCodeCompiler.CompileAssemblyFromFile( objCompilerParameters, files.ToArray() );

我还添加了使其工作所需的 dll 引用。

注意:多亏了里德,我才能够通过以下方式使其足够好地满足我的需求:

 List<string> bamlFiles = Directory.GetFiles( directoryPath, "*.baml", SearchOption.AllDirectories ).ToList();
 bamlFiles.ForEach( x => objCompilerParameters.EmbeddedResources.Add( x ) );

在我的项目中,这已经足够好了。我有一个用于执行语音命令的 .NET 应用程序。一般来说,我有它,所以我可以在更改语音命令时重新编译内存中的程序集更改。我想其中一些不适用于 WPF,但我现在可以在我的内存程序集中使用 WPF windows。

问题在于 WPF 文件不仅是 C#,它们也是 XAML,然后在单独的 MSBuild 任务中编译到 BAML 资源中并作为嵌入式资源包含在内。

如果您想支持此版本的某些版本,您需要将所有引用的 xaml 作为资源包括在内。有关如何使用 CodeDom 执行此操作的详细信息,请参阅 this post

完成后,您还必须确保使用兼容的机制来加载类型。 "normal" C# 编译 xaml/xaml.cs 文件的方式不适用于您的情况,因为它需要将资源预编译为 baml。您必须有效地 "rewrite" C# 类型的代码才能使用不同的机制来加载 XAML - 通常这将通过使用 XamlObjectReaderXamlObjectWriter 来完成在 InitializeComponent 传递期间将 xaml 内容和 "write them" 读入对象。

另一条非常有用的信息位于:The component does not have a resource identified by the uri

据此我创建了一个可以像这样调用的扩展方法:

 // 
 this.LoadViewFromUri( @"/ScriptCode;component/wpf_ui/spywindowviewer.xaml" );
 // InitializeComponent();

注意: 我只是使用错误消息中显示的 uri,例如:

组件 'Dynamic.DragonListForm' 没有由 URI '/ScriptCode;component/wpf_ui/dragonlistform.xaml' 标识的资源。在

扩展方法:

using System;
using System.IO.Packaging;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Navigation;

namespace Extensions
{
   public static class WpfWindowExtensions
   {
      // 
      public static void LoadViewFromUri( this Window window, string baseUri )
      {
         try
         {
            var resourceLocater = new Uri( baseUri, UriKind.Relative );
            // log.Info( "Resource locator is: ")
            var exprCa = ( PackagePart )typeof( Application ).GetMethod( "GetResourceOrContentPart", BindingFlags.NonPublic | BindingFlags.Static ).Invoke( null, new object[] { resourceLocater } );
            var stream = exprCa.GetStream();
            var uri = new Uri( ( Uri )typeof( BaseUriHelper ).GetProperty( "PackAppBaseUri", BindingFlags.Static | BindingFlags.NonPublic ).GetValue( null, null ), resourceLocater );
            var parserContext = new ParserContext
            {
               BaseUri = uri
            };
            typeof( XamlReader ).GetMethod( "LoadBaml", BindingFlags.NonPublic | BindingFlags.Static ).Invoke( null, new object[] { stream, parserContext, window, true } );
         }
         catch( Exception )
         {
            //log
         }
      }
   }
}