views:

615

answers:

2

Given a pack:// URI, what's the best way to tell whether a compiled resource (e.g. a PNG image, compiled with a Build Action of "Resource") actually exists at that URI?

After some stumbling around, I came up with this code, which works but is clumsy:

private static bool CanLoadResource(Uri uri)
{
    try
    {
        Application.GetResourceStream(uri);
        return true;
    }
    catch (IOException)
    {
        return false;
    }
}

(Note that the Application.GetResources documentation is wrong -- it throws an exception if the resource isn't found, rather than returning null like the docs incorrectly state.)

I don't like catching exceptions to detect an expected (non-exceptional) result. And besides, I don't actually want to load the stream, I just want to know whether it exists.

Is there a better way to do this, perhaps with lower-level resource APIs -- ideally without actually loading the stream and without catching an exception?

A: 

Application.GetResourceStream doesn't throw an exception if the resource is not found, it just returns null.

From MSDN :

Return Value

Type: System.Windows.Resources.StreamResourceInfo

A StreamResourceInfo that contains a resource stream for resource data file that is located at the specified Uri. If the resource located at the specified Uri is not found, null is returned.

So you can just do that :

private static bool CanLoadResource(Uri uri)
{
    return Application.GetResourceStream(uri) != null;
}
Thomas Levesque
Huh - apparently the docs are wrong. Try it: create a new WPF app and put this line in the window's constructor: `Application.GetResourceStream(new Uri("pack://application:,,,/foo"));` Then run. It crashes and burns. Debug, view the exception detail, and see the IOException.
Joe White
Indeed, the doc seems to be wrong...
Thomas Levesque
I opened a Connect bug for the documentation error: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=523550 Feel free to confirm it as reproducible...
Joe White
Just did. I also sent feedback from the documentation page
Thomas Levesque
Why the downvote ? I'm not responsible for incorrect documentation...
Thomas Levesque
Unfortunately using System.Windows.Application will not work for Windows Forms applications that host WPF content. If this solution is to be used in a WPF control library it probably isn't sufficient for that reason.
jpierson
A: 

I've found a solution that I'm using which doesn't work directly with a pack Uri but instead looks up a resource by it's resource path. That being said, this example could be modified pretty easily to support a pack URI instead by just tacking on the resource path to the end of a uri which uses the Assembly to formulate the base part of the URI.

public static bool ResourceExists(string resourcePath)
{
    var assembly = Assembly.GetExecutingAssembly();

    return ResourceExists(assembly, resourcePath);
}

public static bool ResourceExists(Assembly assembly, string resourcePath)
{
    return GetResourcePaths(assembly)
        .Contains(resourcePath);
}

public static IEnumerable<object> GetResourcePaths(Assembly assembly)
{
    var culture = System.Threading.Thread.CurrentThread.CurrentCulture;
    var resourceName = assembly.GetName().Name + ".g";
    var resourceManager = new ResourceManager(resourceName, assembly);

    try
    {
        var resourceSet = resourceManager.GetResourceSet(culture, true, true);

        foreach(System.Collections.DictionaryEntry resource in resourceSet)
        {
            yield return resource.Key;
        }
    }
    finally
    {
        resourceManager.ReleaseAllResources();
    }
}
jpierson