9 responses to “A tip for referencing Assets in your WPF application”

  1. Mike Strobel

    You can actually just return GetResourceUri(Path), and the resulting string will be handled exactly as if you had just typed the entire pack URI in the Xaml. That means image and font URIs will still get properly converted (e.g. WPF will cook up an ImageSource for you if you return a URI to an image file).

    The real potential in your solution is that you have the opportunity to do caching. If all of your assets are loaded with this extension, you should consider keeping a lookup table that maps asset paths to weak references of those assets. That way, if they get reused (which many do), you’re not loading them multiple times.

    There is another option to consider. You could register a custom web request handler. If create a web request factory that implements IWebRequestCreate, you can register it to handle a custom URI scheme (see WebRequest.RegisterPrefix(…)). Then you could replace your markup extensions with simple URIs, like “asset:///Images/marker/self_fill.png”. You’ll need to implement a custom WebResponse that overrides GetResponseStream(). I ended up doing this for a project where resources were organized in a virtual file system. I needed to check different relative locations in priority order. It worked out rather well.

  2. Mike Strobel

    Pavan, it seems you’re right about an exception being thrown when you return just the URI. I may have been remembering incorrectly. I think I actually tried a different approach where I used a value converter to convert the “short” path to the fully qualified resource URI, like so:

    Obviously, this isn’t much better than typing the full URI, which is why I went on explore other options.

    As for the caching, I simply meant that if you are using the same image in multiple places in your application (for example), your extension could cache a weak reference when it creates the first BitmapImage, and then return the same cached instance in subsequent requests for the same image (if the reference is still alive). That would save you some video memory in theory. It’s possible that WPF uses a bitmap caching system internally that I’m unaware of.

  3. Mike Strobel

    Whoops, my example Xaml got stripped out:

    [Image Source="{Binding Source='Images/splash.png', Converter={StaticResource ResourcePathConverter}}" /]

  4. Mike Strobel

    A-ha, now I remember how I did it before. I used a Binding inside my markup extension to leverage WPF’s built-in value converters:

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
    var binding = new Binding
    {
    Source = GetResourceUri(Path),
    BindsDirectlyToSource = true
    };
    return binding.ProvideValue(serviceProvider);
    }

  5. Mike Strobel

    I agree, the custom URI scheme is a really fun exercise. Not only is it cool to have WPF “automagically” recognize your own custom URI format, but it’s also the most lightweight implementation. That is, after all, how pack URIs are handled (see PackWebRequest and PackWebResponse for reference). A custom-tailored implementation should be even more efficient and flexible :) . You can also register your handler in your App.config file rather than relying solely on a runtime call to WebRequest.RegisterPrefix().

  6. Yogesh

    Thanks for the article Pavan. I was using the direct link approach till now and my application uses a lot of small images. I changed by solution to use resource extensions and I feel this way is a much better and maintainable way of accessing resources.

    BTW: Returning a BitmapImage at all places caused exceptions for me (specially in custom controls) so I took Mike’s way by using Binding as the return value and solved the issue. Thanks to Mike for the resolution. :)

  7. Steven

    A welcomed extension! If you do not specify the parameter name “Path=” in the XAML, have you seen – and do you know why – VS08 warns that “No constructor for AssetExtension has 1 parameter”? If you include the parameter name “Path=” that warning goes away. Cheers.

Leave a Reply