web-dev-qa-db-ja.com

分離コードからイメージリソースURIを設定する方法

PNGグラフィックをDLLに埋め込み、それを Image controlBitmapImage としてロードしようとしていますが、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からPresentationCorePresentationFrameworkSystem.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に設定します(推奨されるように、たとえば here )。

これまでのところ、このアプリケーションを起動すると、MyClassをインスタンス化し、そのImageインスタンスによって作成されたMyClassを取得するウィンドウが作成されます。その画像は、リソースとして含まれているグラフィックからデータがロードされたBitmapImageで埋められているはずです。

残念ながら、リソースURIに問題があるようです。 MSDNのドキュメント はこれまでのところ役に立っていません。

以下のリソースURIのバリエーションを試しました。

  • 上記のコードサンプルに示されているフォーム-/AssemblyName;component/Path/Filename- here および here が提案されましたが、DirectoryNotFoundExceptionがスローされ、パスの一部C:\ImageResTestLib;component\MyData\SomeStuff\Resources\Img.pngが見つかりませんでした。
  • pack://application:,,,/MyData/SomeStuff/Resources/Img.pngが提案されました hereherehere および here がスローされますが、IOExceptionがスローされ、リソースmydata/somestuff/resources/img.pngが見つかりませんでした。
  • pack://application:,,,/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.pnghere と同様に here と同様に提案されましたが、ImageResTestLib, Culture=neutralまたはその依存関係の1つが見つからなかったことを示すFileNotFoundExceptionをスローします。
  • Resources/Img.png(コードファイルからの相対)は暗黙的に here および here でしたが、C:\Users\myusername\Documents\Test\DOTNET\WPFTest\ImageResTest\bin\Debug\Resources\Img.pngが見つからなかったことを示すDirectoryNotFoundExceptionをスローしました。
  • MyData/SomeStuff/Resources/Img.png(プロジェクトに対して)、また暗黙の here は、前のものと同様に動作します。

これらはどれも機能しないため、ResourceDictionaryに基づいて次の回避策を試しました。

  • SomeStuffフォルダーにMyClassResources.xamlという名前のWPFリソースディクショナリを追加します。
  • そのリソースディクショナリで、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からリソースディクショナリをロードできます(リソースディクショナリのコンテンツを削除すると、ロードは正常に完了します)。ただし、/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.pngのようなパスを使用しても、PNGグラフィックはまだ見つかりません。

何が問題なのですか、どのようにして各リソースをロードできますか(可能であれば、追加のリソースディクショナリなしで)?


編集:いくつかの詳細情報:

  • ドイツ語版のWindows 7 x64を使用しています
  • .NET 4.0クライアントがターゲットフレームワークとして設定されている
  • 念のため、これを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.

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もSharpDevelopanyを示していないが、Resourceとしてマークされているリソースは実際には論理名(私の場合、一時的にビルドアクションをEmbeddedResourceに設定して論理名を変更したときから保持されていました)。論理名は引き続きMSBuildファイルの<LogicalName>要素に表示され、 ILSpy で確認できるものから、thatは実際にリソース名として使用されるものですコンパイルされたアセンブリで。

論理名のようなリソースへの正しい(動作する)リソースURIは、

/MyAssembly;component/LogicalResourceName

(したがって、通常のEmbeddedResourceリソースの場合のように、リソースへのディレクトリパスを置き換えます)

ビルドアクションがResourceに設定されている間、VSまたはSharpDevelopで論理名を変更することはできませんが、リソースを削除してファイルを再度追加し、ビルドアクションをに設定しますリソース、論理名がプロジェクトファイルに存在しなくなるため、ファイル名ベースのURIが再び機能するようになります。同様に、MSBuildファイルから手動で<LogicalName>要素を削除しても機能するはずです。

11
O. R. Mapper

問題の一部は、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;
}

その後、動作します。明らかに、それはMainWindowの分離コードにあります。

1つの小さな変更で、この行を移動します。

Content = img;

最後に、私はあなたと同じDirectoryNotFoundExceptionを取得します。

WPFは、BitmapImageをそのSourceImageプロパティとして割り当てた時点で、そのURIを実際のリソースに解決しようとします。最初の例は、Imageがビジュアルツリーにあり、MainWindow.xamlのベースURIを取得して、そのベース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部分の前に完全修飾アセンブリ表示名が必要です。)

もう1つのオプションは、Application.GetResourceStreamプロパティとともにBitmapImage.StreamSourceを使用することです。ただし、これも機能するURLが必要になるため、以前と同じ問題が発生する可能性があります。プロジェクトのpack://application:,,,/ImageResTestLib;component/MyData/SomeStuff/Resources/Img.pngの機能が停止する原因を突き止めたら、すでに行っている基本的なアプローチで問題ありません。

20
Ian Griffiths