如何从代码后台设置图像资源URI

38 浏览
0 Comments

如何从代码后台设置图像资源URI

我正在尝试将PNG图形嵌入到DLL中,并将其作为BitmapImage加载到Image控件中。但是,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文件,并将其构建操作设置为Resource(如在此处建议),在代码后台使用资源图像

到目前为止,启动此应用程序应该会创建一个窗口,该窗口将实例化MyClass并检索由该MyClass实例创建的Image。该图像应填充用作资源包含的图形的BitmapImage数据。

不幸的是,资源URI似乎出现了问题。迄今为止,MSDN的文档没有提供帮助。

我尝试了以下几种资源URI的变体:

  • 代码示例中所示的形式 - /AssemblyName;component/Path/Filename - 在这里提出herehere,但是会抛出DirectoryNotFoundException异常,表示路径C:\\ImageResTestLib;component\\MyData\\SomeStuff\\Resources\\Img.png的一部分未找到。
  • 建议使用pack://application:,,,/MyData/SomeStuff/Resources/Img.png,在这里herehereherehere,但会抛出IOException异常,表示未找到资源mydata/somestuff/resources/img.png
  • 建议使用pack://application:,,,/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png,在这里here以及here,但会抛出FileNotFoundException异常,表示未找到ImageResTestLib,Culture=neutral或其依赖项之一。
  • (相对于代码文件)暗示Resources/Img.png在这里herehere,但会抛出DirectoryNotFoundException异常,表示未找到C:\\Users\\myusername\\Documents\\Test\\DOTNET\\WPFTest\\ImageResTest\\bin\\Debug\\Resources\\Img.png
  • (相对于项目)暗示MyData/SomeStuff/Resources/Img.png,在这里here,以前一个类似的方式进行,行为类似于前一个。

由于这些都不起作用,我尝试了基于ResourceDictionary的以下解决方法:

  • SomeStuff文件夹中添加名为MyClassResources.xaml的WPF资源字典。
  • 在该资源字典中,添加键值为imgBitmapImage资源。
  • 将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;
                }
            }
        }
    }
    

现在,资源字典可以从指定的URI加载(当删除资源字典的内容时,加载完成)。

我做错了什么,我该如何加载相应的资源(如果可能,不使用额外的资源字典)?


编辑:一些更多的信息:

  • 我使用的是德国Windows 7 x64
  • 将.NET 4.0 Client设置为目标框架
  • 为了确保,我尝试从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文件的元素中,从我在ILSpy中所看到的来看,这实际上是编译后程序集中使用的资源名称。

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

/MyAssembly;component/LogicalResourceName

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

虽然在构建操作设置为Resource的情况下无法更改逻辑名称,但是将资源删除并重新添加文件,然后将构建操作设置为Resource,可以使基于文件名的URI再次工作,因为逻辑名称将不再在项目文件中。同样,手动从MSBuild文件中删除元素也可以有效。

admin 更改状态以发布 2023年5月21日
0