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

O. *_*per 11 wpf resources code-behind

我试图将PNG图形嵌入到DLL中并将其作为一个加载到Image控件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;
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 创建一个名为ImageResTestLib的类库(您可以将默认命名空间设置为ImageResTest,如上所述,因此此处讨论的所有内容都位于相同的根命名空间中).

  • ImageResTestLib中的引用添加到PresentationCore,PresentationFramework,System.XamlWindowsBase.
  • 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;
                }
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • Resources文件夹中添加名为PNG文件Img.png,并设置其生成的行动资源(如建议,例如,在这里).

到目前为止,这么好 - 启动这个应用程序应该创建一个窗口来实例化MyClass和检索Image由该MyClass实例创建的.该图像应该已经填充了一个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:

  • 在文件夹中添加名为MyClassResources.xaml的WPF资源字典SomeStuff.
  • 在该资源管理中,BitmapImage使用密钥添加资源img.
  • 像这样更改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()
            {
                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;
                }
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

现在,可以从指示的URI加载资源字典(当删除资源字典的内容时,加载成功完成).但是,使用类似路径时仍未找到PNG图形/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png.

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


编辑:更多信息:

  • 我使用的是德语Windows 7 x64
  • .NET 4.0 Client被设置为目标框架
  • 为了确保,我尝试在Visual Studio 2010和SharpDevelop 4.3.3中构建和运行它; 两次都导致相同的异常.

FileNotFoundException我根据Ian的代码得到的堆栈跟踪如下:

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.
Run Code Online (Sandbox Code Playgroud)

EDIT2:

添加

Debug.WriteLine(typeof(MyData.SomeStuff.MyClass).Assembly.GetName().FullName);
Run Code Online (Sandbox Code Playgroud)

到主窗口的构造函数导致以下输出:

ImgResTestLib, Version=1.0.5123.16826, Culture=neutral, PublicKeyToken=null
Run Code Online (Sandbox Code Playgroud)

打电话给

Debug.WriteLine(BaseUriHelper.GetBaseUri(this).ToString());
Run Code Online (Sandbox Code Playgroud)

打印以下内容:

pack://application:,,,/ImageResTest;component/window1.xaml
Run Code Online (Sandbox Code Playgroud)

EDIT3:

虽然接受的答案解决了这个问题所描述的问题,但为什么我在实际项目中看不到我的图形的实际原因却完全不同:

虽然VS 2010和SharpDevelop都没有给出任何指示,但标记为Resource的资源实际上具有逻辑名称(在我的情况下,它们保留了我从暂时将构建操作设置为EmbeddedResource并更改逻辑名称时).逻辑名称仍然出现在<LogicalName>MSBuild文件的元素中,并且从我在ILSpy中可以看到的内容,实际用作已编译程序集中的资源名称的内容.

具有逻辑名称的这种资源的正确(工作)资源URI 似乎是

/MyAssembly;component/LogicalResourceName
Run Code Online (Sandbox Code Playgroud)

(因此像通常的EmbeddedResource资源一样,替换资源的目录路径)

虽然在构建操作设置为Resource时无法更改VS或SharpDevelop中的逻辑名称,但删除资源并重新添加文件,然后将构建操作设置为Resource,使基于文件名的URI再次起作用逻辑名称将不再出现在项目文件中.同样,<LogicalName>从MSBuild文件中手动删除元素应该可以工作.

Ian*_*ths 18

部分问题是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;
}
Run Code Online (Sandbox Code Playgroud)

然后它工作.MainWindow显然,那是在代码隐藏中.

通过一个微小的变化,移动这一行:

Content = img;
Run Code Online (Sandbox Code Playgroud)

到最后,然后我DirectoryNotFoundException和你一样.

WPF尝试将该URI解析为实际资源,并将其指定BitmapImage为该Source属性Image.我的第一个例子是有效的,因为Image它位于可视树中,因此它获取了基本URI MainWindow.xaml,并解析了相对于该基URI的资源URI.

如果你真的需要创建它Image之前它与可视树相关联,你有各种选择.您实际上可以在图像上设置基本URI:

img.SetValue(BaseUriHelper.BaseUriProperty, baseUri);
Run Code Online (Sandbox Code Playgroud)

然而,这有点奇怪.构建绝对URI更容易,例如:

bmp.UriSource = new Uri(
    baseUri,
    @"/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png");
Run Code Online (Sandbox Code Playgroud)

这两个当然都假定您知道基URI是什么.您可以通过在MainWindow构造函数中询问来找到它:

public MainWindow()
{
    InitializeComponent();

    var baseUri = BaseUriHelper.GetBaseUri(this);
    ...
Run Code Online (Sandbox Code Playgroud)

在你的情况下,那将是: pack://application:,,,/ImageResTest;component/mainwindow.xaml

这反过来说明了解析的URI应该是什么: pack://application:,,,/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png

有趣的是,你说你试试并得到一个错误.好吧,我正在尝试那个确切的URI,而且我没有收到错误.为了清楚起见,这是我的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;
}
Run Code Online (Sandbox Code Playgroud)

这是我的MainWindow构造函数:

public MainWindow()
{
    InitializeComponent();

    var obj = new MyData.SomeStuff.MyClass(BaseUriHelper.GetBaseUri(this));

    this.Content = obj.Img;
}
Run Code Online (Sandbox Code Playgroud)

这对我有用,遵循了你的指示.如果我理解正确的话,你会看到FileNotFoundException你这样做的时候.这让我想知道你的指示是否遗漏了什么.例如,如果ImageResTestLib被强烈命名,我希望看到这个错误.(如果要引用强命名库中的资源,则需要在部件之前使用完全限定的程序集显示名称;component.)

另一种选择是Application.GetResourceStreamBitmapImage.StreamSource财产一起使用.但同样,这将需要一个有效的URL,因此您可能会遇到与以前相同的问题.一旦你弄清楚你的项目中有什么不同就停止pack://application:,,,/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png了工作,那么你已经拥有的基本方法应该没问题.