如何从代码隐藏设置图像资源URI

时间:2014-01-09 14:17:53

标签: wpf resources code-behind

我正在尝试将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,如上所述,因此这里讨论的所有内容都位于相同的根命名空间中。

  • ImageResTestLib 的引用添加到 PresentationCore PresentationFramework System.Xaml WindowsBase
  • ImageResTest 的引用添加到 ImageResTestLib
  • ImageResTestLib 内,添加文件夹层次结构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 MSDNhere,但会抛出DirectoryNotFoundException,表示路径的一部分{找不到{1}}。
  • C:\ImageResTestLib;component\MyData\SomeStuff\Resources\Img.png被建议herehereherehere,但会引发pack://application:,,,/MyData/SomeStuff/Resources/Img.png说资源{{1}无法找到。
  • IOException也被建议here以及here,但会引发mydata/somestuff/resources/img.pngpack://application:,,,/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png或其中一个依赖项未找到。
  • FileNotFoundException(相对于代码文件)暗示herehere,但会引发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图形。

我做错了什么以及如何加载相应的资源(如果可能,没有额外的资源字典)?


编辑:更多信息:

  • 我使用的是德语版Windows 7 x64
  • 将.NET 4.0客户端设置为目标框架
  • 为了确保,我尝试在Visual Studio 2010和SharpDevelop 4.3.3中构建和运行它;两次都导致同样的例外。

我根据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>元素也应该有效。

1 个答案:

答案 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工作的项目中的不同之处,那么你已经拥有的基本方法应该没问题。

相关问题