我正在尝试将PNG图形嵌入到DLL中,并将其作为Image
control加载到BitmapImage
中。但是,WPF不断抛出异常,说无法找到资源。
首先,一些最小的示例代码和重现问题的步骤:
使用空主窗口创建名为 ImageResTest 的WPF项目(您可以将默认命名空间设置为ImageResTest
)。主窗口的代码隐藏文件应如下所示:
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;
}
}
}
创建一个名为 ImageResTestLib 的类库(您可以将默认命名空间设置为ImageResTest
,如上所述,因此这里讨论的所有内容都位于相同的根命名空间中。
MyData/SomeStuff/Resources
。在SomeStuff
文件夹中,添加以下文件 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;
}
}
}
}
在Resources
文件夹中,添加名为Img.png
的PNG文件,并将其构建操作设置为资源(如建议的那样,{{3} })。
到目前为止,非常好 - 启动此应用程序应创建一个窗口,实例化MyClass
并检索由Image
实例创建的MyClass
。该图像应该填充BitmapImage
,其数据是从作为资源包含的图形加载的。
不幸的是,资源URI似乎有问题。到目前为止here没有帮助。
我尝试过以下资源URI变种:
/AssemblyName;component/Path/Filename
- 已被建议documentation on MSDN和here,但会抛出DirectoryNotFoundException
,表示路径的一部分{找不到{1}}。C:\ImageResTestLib;component\MyData\SomeStuff\Resources\Img.png
被建议here,here,here和here,但会引发pack://application:,,,/MyData/SomeStuff/Resources/Img.png
说资源{{1}无法找到。IOException
也被建议here以及here,但会引发mydata/somestuff/resources/img.png
说pack://application:,,,/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png
或其中一个依赖项未找到。 FileNotFoundException
(相对于代码文件)暗示here和here,但会引发ImageResTestLib, Culture=neutral
,表示未找到Resources/Img.png
。DirectoryNotFoundException
(相对于项目),也暗示为here,其行为类似于前一个。由于这些都不起作用,我尝试了基于C:\Users\myusername\Documents\Test\DOTNET\WPFTest\ImageResTest\bin\Debug\Resources\Img.png
:
MyData/SomeStuff/Resources/Img.png
文件夹中添加名为 MyClassResources.xaml 的WPF资源字典。ResourceDictionary
添加SomeStuff
资源。更改 MyClass.cs 的内容,如下所示:
BitmapImage
现在,可以从指示的URI加载资源字典(当删除资源字典的内容时,加载成功完成)。但是,使用img
等路径时仍未找到PNG图形。
我做错了什么以及如何加载相应的资源(如果可能,没有额外的资源字典)?
编辑:更多信息:
我根据here代码获得的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
EDIT2:
添加
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.
致电
Debug.WriteLine(typeof(MyData.SomeStuff.MyClass).Assembly.GetName().FullName);
打印以下内容:
ImgResTestLib, Version=1.0.5123.16826, Culture=neutral, PublicKeyToken=null
EDIT3:
虽然接受的答案解决了这个问题所描述的问题,但是为什么我在实际项目中看不到我的图形的实际原因却完全不同:
虽然 VS 2010和SharpDevelop 都没有任何指示,但标记为资源的资源实际上有一个逻辑名称(在我的情况下,当我暂时将构建操作设置为 EmbeddedResource 并更改逻辑名称时,它们保留了它。逻辑名称仍显示在MSBuild文件的Debug.WriteLine(BaseUriHelper.GetBaseUri(this).ToString());
元素中,并且从我在Ian中看到的内容 实际上用作编译时的资源名称组装
具有逻辑名称的此类资源的正确(工作)资源URI似乎是
pack://application:,,,/ImageResTest;component/window1.xaml
(因此替换资源的目录路径,就像 EmbeddedResource 资源一样)
虽然在构建操作设置为资源时无法更改VS或SharpDevelop中的逻辑名称,但删除资源并重新添加文件,然后将构建操作设置为< em> Resource ,使基于文件名的URI再次起作用,因为逻辑名称将不再出现在项目文件中。同样,从MSBuild文件中手动删除<LogicalName>
元素也应该有效。
答案 0 :(得分:16)
部分问题是WPF没有用于解析该URL的上下文。它是一个相对URL,通常,它将相对于使用它的XAML内容的基URI进行解析。如果我在此代码中使用完全相同的URL:
public MainWindow()
{
InitializeComponent();
var img = new Image();
Content = img;
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;
}
然后它的工作原理。那显然是MainWindow
的代码隐藏。
通过一个微小的改变,移动这一行:
Content = img;
到最后,然后我和你一样DirectoryNotFoundException
。
WPF尝试在您将BitmapImage
指定为Source
的{{1}}属性的位置将该URI解析为实际资源。我的第一个示例有效,因为Image
位于可视化树中,因此它会获取Image
的基URI,并相对于该基URI解析该资源URI。
如果你真的需要在它与可视树相关联之前创建MainWindow.xaml
,你就有了各种选择。您实际上可以在图像上设置基本URI:
Image
但是,这有点奇怪。构建绝对URI更容易,例如:
img.SetValue(BaseUriHelper.BaseUriProperty, baseUri);
这两个当然都假设您知道基URI是什么。您可以通过在MainWindow构造函数中询问来找到它:
bmp.UriSource = new Uri(
baseUri,
@"/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png");
在您的情况下,那将是:public MainWindow()
{
InitializeComponent();
var baseUri = BaseUriHelper.GetBaseUri(this);
...
这反过来说明了解析的URI应该是什么:pack://application:,,,/ImageResTest;component/mainwindow.xaml
有趣的是,你说你试试并得到一个错误。好吧,我正在尝试那个确切的URI,而且我没有收到错误。为了清楚起见,这是我的pack://application:,,,/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png
构造函数的修改版本:
MyClass
这是我的public MyClass(Uri baseUri)
{
img = new Image();
var bmp = new BitmapImage();
bmp.BeginInit();
bmp.UriSource = new Uri(baseUri, @"/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png");
bmp.EndInit();
img.Source = bmp;
img.Width = bmp.PixelWidth;
}
构造函数:
MainWindow
这对我有用,遵循了你的指示。如果我理解正确,那么当你这样做时,你会看到public MainWindow()
{
InitializeComponent();
var obj = new MyData.SomeStuff.MyClass(BaseUriHelper.GetBaseUri(this));
this.Content = obj.Img;
}
。这让我想知道你的指示是否遗漏了什么。例如,如果FileNotFoundException
被强烈命名,我希望看到这个错误。 (如果要引用强命名库中的资源,则需要在ImageResTestLib
部分之前使用完全限定的程序集显示名称。)
另一种选择是使用;component
以及Application.GetResourceStream
属性。但同样,这将需要一个有效的URL,因此您可能会遇到与以前相同的问题。一旦你找到了阻止BitmapImage.StreamSource
工作的项目中的不同之处,那么你已经拥有的基本方法应该没问题。