使用 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 - 通常这将通过使用 XamlObjectReader
和 XamlObjectWriter
来完成在 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
}
}
}
}
我有一个项目,我使用 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 - 通常这将通过使用 XamlObjectReader
和 XamlObjectWriter
来完成在 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
}
}
}
}