Как установить URI ресурса изображения из кода

я пытаюсь встроить png-графику в DLL и загрузить ее в Image управления как BitmapImage. Однако WPF продолжает выдавать исключение, говорящее, что ресурс не может быть найден.

во-первых, какой-то минимальный пример кода и шаги для воспроизведения проблемы:

  • создайте проект WPF с именем ImageResTest с пустым главным окном (вы можете установить пространство имен по умолчанию в 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 to сборок presentationcore, PresentationFramework, .В XAML и WindowsBase.
  • добавить ссылку из ImageResTest to 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 папка, добавьте PNG-файл с именем Img.png и установите его действие сборки в ресурс (как предлагалось, например,здесь).

до сих пор, так хорошо-запуск этого приложения должен создать окно, которое создает экземпляры MyClass и получает Image созданный что MyClass экземпляра. Это изображение должно было быть заполнено BitmapImage чьи данные были загружены из графика, включенного в качестве ресурса.

к сожалению, кажется, что-то не так с URI ресурса. The документация на В MSDN пока не помогло.

я пробовал следующие варианты URIs ресурса:

  • форма, изображенная в примере кода выше -/AssemblyName;component/Path/Filename - было предложено здесь и здесь, но a DirectoryNotFoundException бросают, говоря, что часть пути C:ImageResTestLib;componentMyDataSomeStuffResourcesImg.png не найдено.
  • pack://application:,,,/MyData/SomeStuff/Resources/Img.png было предложено здесь, здесь, здесь и здесь, но бросает IOException сказав, что ресурс mydata/somestuff/resources/img.png не удалось найти.
  • pack://application:,,,/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png также было предложено здесь, а также здесь, но бросает!--28--> сказав, что ImageResTestLib, Culture=neutral или одна из его зависимостей не найдена.
  • Resources/Img.png (относительно из файла кода) подразумевалось здесь и здесь, но бросает!--22--> сказав, что C:UsersmyusernameDocumentsTestDOTNETWPFTestImageResTestbinDebugResourcesImg.png не найдено.
  • MyData/SomeStuff/Resources/Img.png (относительно проект), также как подразумевается здесь, ведет себя аналогично предыдущей.

ни один из них не будет работать, я попробовал следующее решение на основе ResourceDictionary:

  • добавить словарь ресурсов WPF с именем MyClassResources.в XAML на .
  • в этом ресурсе dictioanry добавьте 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;
                }
            }
        }
    }
    

теперь словарь ресурсов можно загрузить из указанного URI (при удалении содержимого словаря ресурсов загрузка завершается успешно). Однако png-графика все еще не найдена при использовании пути /ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png.

что я делаю неправильно и как я могу загрузить соответствующие ресурсы (если это возможно, без дополнительных ресурсов словарь)?


EDIT: дополнительная информация:

  • я использую немецкий Windows 7 x64
  • клиент .NET 4.0 установлен в качестве целевой платформы
  • чтобы убедиться, я попытался создать и запустить это как из Visual Studio 2010, так и из SharpDevelop 4.3.3; оба раза в результате одного и того же исключения.

stacktrace 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:UsersusernameDocumentsTestDOTNETWPFTestImgResTestLibMyDataSomeStuffMyClass.cs:Zeile 36.
   bei ImageResTest.Window1..ctor() in c:UsersusernameDocumentsTestDOTNETWPFTestImageResTestWindow1.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:UsersusernameDocumentsTestDOTNETWPFTestImageResTestobjDebugApp.g.cs:Zeile 0.

EDIT2:

добавлять

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

EDIT3:

хотя принятый ответ решает проблему, описанную этим вопросом, фактическая причина, по которой я не мог видеть свою графику в своем фактическом проекте было совсем другое:

пока ни VS 2010, ни SharpDevelop дать любой указание на это, ресурсы помечены как ресурс на самом деле имеют логическое имя (в моем случае они сохранили его, когда я предварительно установил действие сборки в EmbeddedResource и изменил логическое имя). Логическое имя по-прежнему отображается в <LogicalName> элемент в файле MSBuild и из того, что я вижу в ILSpy, это - это то, что фактически используется в качестве имени ресурса в скомпилированной сборке.

правильный (рабочий) URI ресурса для такого ресурса с логическое имя кажется

/MyAssembly;component/LogicalResourceName

(заменив тем самым путь к каталогу ресурса, как обычно EmbeddedResource ресурсы)

пока невозможно изменить логическое имя в VS или SharpDevelop во время сборки действие ресурс, удаление ресурса и повторное добавление файла, а затем установка действия сборки в ресурс, заставляет URIs на основе имени файла работать снова, так как логическое имя больше не будет в файле проекта. Аналогично, удаление <LogicalName> элемент вручную из файла MSBuild должен работать.

1 ответов


часть проблемы заключается в том, что WPF не имеет контекста, с которым можно решить этот URL. Это относительный URL-адрес, и обычно он будет разрешен относительно базового URI содержимого XAML, в котором он используется. Если я использую точно такой же 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;
}

затем он работает. Это в codebehind для MainWindow, это очевидно.

С одним крошечным изменением, двигая эту строку:

Content = img;

до конца, тогда я получаю то же самое DirectoryNotFoundException as вы.

WPF пытается разрешить этот URI фактическому ресурсу в точке, в которой вы назначаете BitmapImage как Source свойства Image. Мой первый пример работает, потому что Image находится в визуальном дереве, и поэтому он подбирает базовый URI MainWindow.xaml и разрешает этот URI ресурса относительно этого базового URI.

Если вам действительно нужно создать Image прежде чем он будет связан с визуальным деревом, у вас есть различные варианты. Вы могли бы установить базовый URI на изображении:

img.SetValue(BaseUriHelper.BaseUriProperty, baseUri);

однако, это немного странно. Проще просто построить абсолютный URI, например:

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

оба они, конечно, предполагают, что вы знаете, что такое базовый URI. Вы можете узнать это, спросив в своем конструкторе MainWindow:

public MainWindow()
{
    InitializeComponent();

    var baseUri = BaseUriHelper.GetBaseUri(this);
    ...

в вашем случае, это будет: 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;
}

а вот и мой MainWindow конструктора:

public MainWindow()
{
    InitializeComponent();

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

    this.Content = obj.Img;
}

это работает для меня, следуя вашим инструкциям. Если я правильно вас понял, вы видите FileNotFoundException когда вы делаете это. Это заставляет меня задуматься, не пропустили ли ваши инструкции что-то. Например, Я ожидал бы увидеть эту ошибку, если ImageResTestLib было сильно названо. (Если вы хотите ссылаться на ресурс в строго именованной библиотеке, перед ;component часть.)

другой вариант - использовать Application.GetResourceStream вместе с BitmapImage.StreamSource собственность. Но опять же, для этого понадобится рабочий URL-адрес, поэтому вы, вероятно, столкнетесь с той же проблемой, что и раньше. Как только вы выясните, что отличается в вашем проекте, который останавливается pack://application:,,,/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png от работы, тогда основной подход у тебя уже все должно быть хорошо.