2014-01-09 3 views
9

Я пытаюсь встроить PNG-графику в DLL и загрузить ее в Image control как BitmapImage. Однако WPF продолжает бросать исключение, говоря, что ресурс не найден.Как установить URI ресурса ресурса из Code-Behind

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

  • Создание проекта 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 к 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, добавьте файл PNG с именем Img.png и установить его сборки действие на ресурс (как это было предложено , например, here).

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

К сожалению, в URI ресурса, похоже, что-то не так. documentation on MSDN пока не помог.

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

  • форма изображена в примере кода выше - /AssemblyName;component/Path/Filename - было предложено here и here, но DirectoryNotFoundException брошено, говоря, что часть пути C:\ImageResTestLib;component\MyData\SomeStuff\Resources\Img.png не найден.
  • pack://application:,,,/MyData/SomeStuff/Resources/Img.png было предложено here, here, here и here, но бросает IOException о том, что ресурс mydata/somestuff/resources/img.png не может быть найден.
  • pack://application:,,,/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.png также было предложено here, а также here, но бросает FileNotFoundException говоря, что ImageResTestLib, Culture=neutral или один из его зависимостей не найден.
  • Resources/Img.png (относительный от файла кода) подразумевался here и here, но выбрасывает DirectoryNotFoundException, заявив, что C:\Users\myusername\Documents\Test\DOTNET\WPFTest\ImageResTest\bin\Debug\Resources\Img.png не найден.
  • MyData/SomeStuff/Resources/Img.png (относительно проекта), также как подразумевается here, ведет себя аналогично предыдущему.

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

  • Добавить словарь WPF ресурсов с именем MyClassResources.xaml в папке 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; 
          } 
         } 
        } 
    } 
    

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

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


EDIT: Некоторые больше информации:

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

StackTrace из 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. 

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, в то время как для действия сборки установлено значение Resource, удаление ресурса и повторное добавление файла, а затем установка действия сборки ion до Ресурс, заставляет URI с именами файлов работать снова, так как логическое имя больше не будет в файле проекта. Аналогично, удаление элемента <LogicalName> вручную из файла MSBuild должно работать.

+0

Ваши инструкции помещают 'MyClass' в компонент ImageRestTestLib, но исходный код показывает пространство имен как« ImageRestTest », подразумевая, что он находится в основном проекте« ImageRestTest ». Очевидно, нет ничего, что мешало бы вам использовать разные пространства имен, но я хотел проверить, произошла ли ошибка в инструкциях. –

+0

@IanGriffiths: Нет, это намеренно, поэтому все здесь находится в одном корневом пространстве имен. Спасибо за замечание; Я добавил комментарий в описании выше. –

+2

невероятный ...HOURS потратил впустую, пытаясь заставить это работать, и, в конце концов, удалив изображение и добавив его обратно, затем установив его на ресурс, «волшебным образом» работал ... Мне нравится, как я, кажется, создаю рабочее приложение за час, но мне понадобится целый день, чтобы вставить в него изображение ...! – Rob

ответ

16

Часть проблемы заключается в том, что 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; 
} 

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

С одной крохотной изменения, перемещаясь эту строку:

Content = img; 

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

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, тогда базовый подход, который у вас уже есть, должен быть прекрасным.

+0

До базового URI все, что я вижу, совпадает с тем, что вы пишете. Howevr, с вашим альтернативным конструктором MyClass (на самом деле, с этим кодом конструктора и, я вижу «FileNotFoundException», указывающий, что * ImageResTestLib, Culture = neutral * или одна из его зависимостей не найдена (см. Мое редактирование вопроса). Ни одна из задействованных сборок не имеет сильного имени. Продолжайте пробовать еще кое-что. –

+0

О, один момент. Я также попробовал ваш самый первый пример кода (модифицированный конструктор MainWindow), и даже это приводит к тому же исключение –

+0

Не могли бы вы попробовать пару вещей: 1: добавить 'Debug.WriteLine (typeof (MyData.SomeStuff.MyClass) .Assembly.GetName(). FullName);' в конструктор MainWindow и показать, что он печатает? , что такое 'BaseUri', о котором сообщает' MainWindow'? Наконец, вы могли бы настроить Visual Studio на разрыв при вызове 'XamlParseException', а затем использовать стек вызовов, чтобы узнать, что аргумент' AssemblyName assemblyRef' находится в вызове ' Assembly.Load'? –

Смежные вопросы