Search code examples
wpfxamlc#-4.0dynamicresource

Images in .ico as DynamicResource


Situation looks like that:

  • I have many icons in application and they are used in few different sizes.
  • I use icons as DynamicResource for example like that:

    <igRibbon:MenuTool (...) LargeImage="{DynamicResource IconAdd}" />

    <s:Item (...) Icon="{DynamicResource IconAdd}"/>

  • Some of icons are in .xaml and some in .png format

  • I add new icons for example like that: <BitmapImage x:Key="IconAdd" UriSource="../Icons/IconAdd.png" />

The problem:
I would like to have icons in .ico format that I can use as DynamicResource.

I want images in .ico because this file format allows to have few different image sizes in one file. Icons in .xaml are fully resizable but they took to much time to load (because I have really a lot of them!).

Is it possible to add .ico file as DynamicResource and add x:key to it?
Even if I somehow add those .ico images, will they change size (depending on how much place they have)?


Solution

  • Yes, it is possible to add .ico files as a DynamicResource (and of course, because it is a resource, it must have an x:Key).

    They will not automatically change size, however. Each size is extractable from the .ico like this, in which I make an Image for each of the icon's frames set to the exact size of the frame, and then add that Image to a StackPanel called imageStack:

    var iconUri = new URI( "pack://application:,,,/MyIcon.ico", UriKind.RelativeOrAbsolute );
    var iconDecoder = new IconBitmapDecoder( iconUri,
        BitmapCreationOptions.None, BitmapCacheOption.Default );
    
    foreach ( var frame in iconDecoder.Frames ) {
        var img = new Image(){
            Height = frame.PixelHeight,
            Width = frame.PixelWidth,
            Source = frame }
        imageStack.Children.Add( img );
    }
    

    When you just use the .ico directly, it will choose whichever frame has the largest pixel resolution and resize that bitmap based on the size of the Image control, or whatever content alignment/sizing properties are in place for your Image, Button, or other control that is displaying it.

    A few options for controlling which frame is added that aren't complete, but may serve as ideas towards a solution:

    • Programmatically split apart the icon into BitmapFrames and add them to the ResourceDictionary with keys like 'MyIcon16', 'MyIcon32', and so on.
    • Create a MarkupExtension or IValueConverter to extract a frame matching certain criteria such as index or size.
    • Since you are using DynamicResource, you can change which frame is associated with a particular resource key at any time.
    • You could control it by scope. You could have the 32x32 frame as a resource in the Window's ResourceDictionary with the key 'MyIcon', and have the 64x64 frame as a resource with the same key in a different scope, such as a Grid within that Window. Anything using {DynamicResource MyIcon} in the Grid will show the 64x64 frame, while everything else in the window shows the 32x32 frame.