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

Wpf 如何从代码隐藏设置图像资源URI,wpf,resources,code-behind,Wpf,Resources,Code Behind,我正在尝试将PNG图形嵌入到DLL中,并将其作为一个文件加载到。但是,WPF不断抛出异常,表示找不到资源 首先,介绍一些最简单的示例代码和重现问题的步骤: 创建一个名为ImageResTest的WPF项目,主窗口为空(您可以将默认名称空间设置为ImageResTest)。主窗口的代码隐藏文件应如下所示: using System; using System.Windows; using System.Windows.Controls; namespace ImageResTest {

我正在尝试将PNG图形嵌入到DLL中,并将其作为一个文件加载到。但是,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;
            }
        }
    }
    
    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的类库(您可以将默认名称空间设置为
    ImageResTest
    ,如上所述,因此此处讨论的所有内容都在同一根名称空间中)

  • 将ImageResTestLib中的引用添加到PresentationCore、PresentationFramework、System.Xaml和WindowsBase
  • 将引用从ImageResTest添加到ImageResTestLib
  • 在ImageResTestLib中,添加文件夹层次结构
    MyData/SomeStuff/Resources
  • SomeStuff
    文件夹中,添加以下文件MyClass.cs:

  • Resources
    文件夹中,添加一个名为
    Img.PNG
    的PNG文件,并将其构建操作设置为Resource(例如,根据建议)

到目前为止,一切顺利-启动此应用程序应创建一个窗口,实例化
MyClass
,并检索由该
MyClass
实例创建的
Image
。该图像应已填充
位图图像
,其数据是从作为资源包含的图形加载的

不幸的是,资源URI似乎有问题。到目前为止,这一政策没有起到任何作用

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

  • 上面代码示例中描述的表单-
    /AssemblyName;建议使用组件/Path/Filename
    ,但抛出了一个
    DirectoryNotFoundException
    ,表示路径的一部分
    C:\imagerestlib;找不到component\MyData\SomeStuff\Resources\Img.png
  • pack://application:,,,,/MyData/sometuff/Resources/Img.png
    ,但抛出一个
    IOException
    ,表示找不到资源
    MyData/sometuff/Resources/Img.png
  • pack://application:,,,/ImageResTestLib;还建议使用component/MyData/sometuff/Resources/Img.png
    ,但抛出
    FileNotFoundException
    ,表示未找到
    imagerestlib,Culture=neutral
    或其依赖项之一
  • Resources/Img.png
    (与代码文件相对)是隐含的,但抛出
    DirectoryNotFoundException
    ,表示未找到
    C:\Users\myusername\Documents\Test\DOTNET\WPFTest\imagerest\bin\Debug\Resources\Img.png
  • MyData/SomeStuff/Resources/Img.png
    (相对于项目),同样暗示,其行为与前一个类似
由于这些都不起作用,我尝试了以下基于
ResourceDictionary
的解决方法:

  • SomeStuff
    文件夹中添加名为MyClassResources.xaml的WPF资源字典
  • 在该资源词典中,使用键
    img
    添加
    BitmapImage
    资源
  • 更改MyClass.cs的内容如下:

    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;
            }
        }
    }
    
    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加载资源字典(当删除资源字典的内容时,加载成功完成)。但是,当使用类似于
/imagerestlib的路径时,仍然找不到PNG图形;component/MyData/SomeStuff/Resources/Img.png

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


编辑:更多信息:

  • 我使用的是德国Windows 7 x64
  • .NET 4.0客户端被设置为目标框架
  • 为了确保这一点,我尝试在VisualStudio2010和SharpDevelop 4.3.3中构建和运行它;两次都会导致相同的异常
我根据的代码获取的
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.
向主窗口的构造函数调用将产生以下输出:

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都没有给出任何指示,但标记为资源的资源实际上有一个逻辑名称(在我的例子中,他们从我尝试将构建操作设置为EmbeddedResource并更改逻辑名称时就保留了该名称)。逻辑名称仍然出现在MSBuild文件的
元素中,从中可以看到,这实际上是编译程序集中用作资源名称的名称

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

/MyAssembly;component/LogicalResourceName
(从而替换资源的目录路径,与嵌入资源的情况一样)


虽然无法在VS或SharpDevelop中将逻辑名称更改为“生成操作”设置为“资源”,但删除资源并重新添加文件,然后将“生成操作”设置为“资源”,会使基于文件名的URI再次工作,因为该逻辑名称将不再位于项目文件中。同样,从MSBuild文件中手动删除
元素也应该可以。部分问题在于WPF没有上下文来解析该URL。它是一个相对URL,通常,它将相对于使用它的XAML内容的基本URI进行解析。如果我使用的URL与您在此代码中开始使用的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;
}
然后它就起作用了。这在
MainWin的代码隐藏中
Content = img;
img.SetValue(BaseUriHelper.BaseUriProperty, baseUri);
bmp.UriSource = new Uri(
    baseUri,
    @"/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png");
public MainWindow()
{
    InitializeComponent();

    var baseUri = BaseUriHelper.GetBaseUri(this);
    ...
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;
}
public MainWindow()
{
    InitializeComponent();

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

    this.Content = obj.Img;
}