我正在尝试将一个PNG图形嵌入到DLL中,并将其作为BitmapImage
加载到Image
控件中。然而,WPF一直抛出异常,称找不到资源。
首先,以下是一些最小化的示例代码和重现问题的步骤:
Create a WPF project named ImageResTest with an empty main window (you can set the default namespace to
ImageResTest
). The code-behind file of the main window should look like this:using System; using System.Windows; using System.Windows.Controls; namespace ImageResTest { public partial class Window1 : Window { public Window1() { InitializeComponent(); var obj = new MyData.SomeStuff.MyClass(); this.Content = obj.Img; } } }
Create a class library named ImageResTestLib (you can set the default namespace to
ImageResTest
, as above, so everything discussed here is in the same root namespace).- Add references from ImageResTestLib to PresentationCore, PresentationFramework, System.Xaml and WindowsBase.
- Add a reference from ImageResTest to ImageResTestLib.
- Inside ImageResTestLib, add the folder hierarchy
MyData/SomeStuff/Resources
. In the
SomeStuff
folder, add the following file MyClass.cs:using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; namespace ImageResTest.MyData.SomeStuff { public class MyClass { public MyClass() { img = new Image(); { var bmp = new BitmapImage(); bmp.BeginInit(); bmp.UriSource = new Uri(@"/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png", UriKind.RelativeOrAbsolute); bmp.EndInit(); img.Source = bmp; img.Width = bmp.PixelWidth; } } private Image img; public Image Img { get { return img; } } } }
In the
Resources
folder, add a PNG file namedImg.png
and set its build action to Resource (as suggested, for example, here).
MyClass
并检索由该MyClass
实例创建的Image
。该图像应填充了一个BitmapImage
,其数据从作为资源包含的图形中加载。不幸的是,似乎资源URI存在问题。至今MSDN文档也没有提供帮助。
我尝试了以下几种变体的资源URI:
上面示例代码中所描述的表单 -
/AssemblyName;component/Path/Filename
- 建议在这里和这里使用,但会抛出DirectoryNotFoundException
异常,表示未找到路径的一部分C:\ImageResTestLib;component\MyData\SomeStuff\Resources\Img.png
。建议在这里、这里、这里和这里使用
pack://application:,,,/MyData/SomeStuff/Resources/Img.png
,但会抛出IOException
异常,表示无法找到资源mydata/somestuff/resources/img.png
。也建议在这里和这里使用
pack://application:,,,/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png
,但会抛出FileNotFoundException
异常,表示未找到ImageResTestLib, Culture=neutral
或其依赖项之一。
Resources/Img.png
(相对于代码文件)在这里和这里中被暗示使用,但会抛出DirectoryNotFoundException
异常,表示未找到C:\Users\myusername\Documents\Test\DOTNET\WPFTest\ImageResTest\bin\Debug\Resources\Img.png
。
MyData/SomeStuff/Resources/Img.png
(相对于项目)也被暗示在这里中使用,行为类似于前一个。
由于这些方法都无法起作用,我尝试了以下基于ResourceDictionary
的解决方法:
- Add a WPF resource dictionary named MyClassResources.xaml in the
SomeStuff
folder. - In that resource dictioanry, add a
BitmapImage
resource with the keyimg
. Change the contents of MyClass.cs like this:
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; namespace ImageResTest.MyData.SomeStuff { public class MyClass { public MyClass() { ResourceDictionary dict = new ResourceDictionary(); dict.Source = new Uri("/ImgResTestLib;component/MyData/SomeStuff/MyClassResources.xaml", UriKind.RelativeOrAbsolute); img = new Image(); { var bmp = (BitmapImage)dict["img"]; img.Source = bmp; img.Width = bmp.PixelWidth; } } private Image img; public Image Img { get { return img; } } } }
/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png
这样的路径时,仍然找不到PNG图形。我做错了什么?如何加载相应的资源(如果可能,不使用额外的资源字典)?
编辑:更多信息:
- 我使用的是德语Windows 7 x64
- .NET 4.0客户端被设置为目标框架
- 为了确保,我尝试了在Visual Studio 2010和SharpDevelop 4.3.3中构建和运行这个程序,两次都导致相同的异常。
基于Ian的代码,我得到的FileNotFoundException
的堆栈跟踪如下:
System.Windows.Markup.XamlParseException: Zeilennummer "3" und Zeilenposition "2" von "Durch den Aufruf des Konstruktors für Typ "ImageResTest.Window1", der den angegebenen Bindungseinschränkungen entspricht, wurde eine Ausnahme ausgelöst.". ---> System.IO.FileNotFoundException: Die Datei oder Assembly "ImageResTestLib, Culture=neutral" oder eine Abhängigkeit davon wurde nicht gefunden. Das System kann die angegebene Datei nicht finden.
bei System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
bei System.Reflection.RuntimeAssembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
bei System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
bei System.Reflection.Assembly.Load(AssemblyName assemblyRef)
bei System.Windows.Navigation.BaseUriHelper.GetLoadedAssembly(String assemblyName, String assemblyVersion, String assemblyKey)
bei MS.Internal.AppModel.ResourceContainer.GetResourceManagerWrapper(Uri uri, String& partName, Boolean& isContentFile)
bei MS.Internal.AppModel.ResourceContainer.GetPartCore(Uri uri)
bei System.IO.Packaging.Package.GetPartHelper(Uri partUri)
bei System.IO.Packaging.Package.GetPart(Uri partUri)
bei System.IO.Packaging.PackWebResponse.CachedResponse.GetResponseStream()
bei System.IO.Packaging.PackWebResponse.GetResponseStream()
bei System.IO.Packaging.PackWebResponse.get_ContentType()
bei System.Windows.Media.Imaging.BitmapDecoder.SetupDecoderFromUriOrStream(Uri uri, Stream stream, BitmapCacheOption cacheOption, Guid& clsId, Boolean& isOriginalWritable, Stream& uriStream, UnmanagedMemoryStream& unmanagedMemoryStream, SafeFileHandle& safeFilehandle)
bei System.Windows.Media.Imaging.BitmapDecoder.CreateFromUriOrStream(Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy, Boolean insertInDecoderCache)
bei System.Windows.Media.Imaging.BitmapImage.FinalizeCreation()
bei System.Windows.Media.Imaging.BitmapImage.EndInit()
bei ImageResTest.MyData.SomeStuff.MyClass..ctor(Uri baseUri) in C:\Users\username\Documents\Test\DOTNET\WPFTest\ImgResTestLib\MyData\SomeStuff\MyClass.cs:Zeile 36.
bei ImageResTest.Window1..ctor() in c:\Users\username\Documents\Test\DOTNET\WPFTest\ImageResTest\Window1.xaml.cs:Zeile 17.
--- End of inner exception stack trace ---
bei System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri)
bei System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader, Boolean skipJournaledProperties, Object rootObject, XamlAccessLevel accessLevel, Uri baseUri)
bei System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream)
bei System.Windows.Application.LoadBamlStreamWithSyncInfo(Stream stream, ParserContext pc)
bei System.Windows.Application.LoadComponent(Uri resourceLocator, Boolean bSkipJournaledProperties)
bei System.Windows.Application.DoStartup()
bei System.Windows.Application.<.ctor>b__1(Object unused)
bei System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
bei MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
bei System.Windows.Threading.DispatcherOperation.InvokeImpl()
bei System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
bei System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
bei System.Windows.Threading.DispatcherOperation.Invoke()
bei System.Windows.Threading.Dispatcher.ProcessQueue()
bei System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
bei MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
bei MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
bei System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
bei MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
bei System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
bei MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
bei MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
bei System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
bei System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
bei System.Windows.Threading.Dispatcher.Run()
bei System.Windows.Application.RunDispatcher(Object ignore)
bei System.Windows.Application.RunInternal(Window window)
bei System.Windows.Application.Run(Window window)
bei System.Windows.Application.Run()
bei ImageResTest.App.Main() in c:\Users\username\Documents\Test\DOTNET\WPFTest\ImageResTest\obj\Debug\App.g.cs:Zeile 0.
编辑2:
添加
Debug.WriteLine(typeof(MyData.SomeStuff.MyClass).Assembly.GetName().FullName);
将代码传递给主窗口的构造函数会产生以下输出:
ImgResTestLib, Version=1.0.5123.16826, Culture=neutral, PublicKeyToken=null
调用
Debug.WriteLine(BaseUriHelper.GetBaseUri(this).ToString());
打印以下内容:
pack://application:,,,/ImageResTest;component/window1.xaml
编辑3:
虽然被接受的答案解决了这个问题所描述的问题,但我在实际项目中无法看到图形的实际原因却完全不同:
虽然VS 2010和SharpDevelop都没有给出任何提示,但标记为Resource的资源实际上有一个逻辑名称(在我的情况下,它们保留了我在试探性地将构建操作设置为EmbeddedResource并更改逻辑名称时的名称)。逻辑名称仍然出现在MSBuild文件中的<LogicalName>
元素中,从我在ILSpy中看到的内容来看,那就是编译后程序集中实际使用的资源名称。
这样一个带有逻辑名称的资源的正确(有效)资源URI似乎是:
/MyAssembly;component/LogicalResourceName
(因此,像嵌入式资源一样替换资源的目录路径)
虽然在构建操作设置为Resource时无法在VS或SharpDevelop中更改逻辑名称,但是删除资源并重新添加文件,然后将构建操作设置为Resource,使基于文件名的URI再次起作用,因为逻辑名称不再在项目文件中。同样,手动从MSBuild文件中删除<LogicalName>
元素也应该可以起作用。
MyClass
放在ImageRestTestLib
组件中,但是你的源代码显示命名空间为ImageRestTest
,这意味着它在主ImageRestTest
项目中。显然,没有什么阻止你使用不同的命名空间,但我想检查一下是否有指示上的错误。 - Ian Griffiths