3

I'm designing my first major app using WPF/C# and I'm trying to understand referencing Resources better.

Many guides tell me to use the first way I am failing to use.

  • I design a resource in illustrator
  • make it 96 dpi,
  • save it as a png,

Then I:

  • Open Resources.resx
  • Click Add Resource ->Image
  • Select my existing PNG called "SuperInt_Alert"
  • Change the build type of that new png to Resource

Then when I try to show the image in my xaml, I use this:

xmlns:prop="clr-namespace:Stark.Properties"

<...code...>

<Image Source="{x:Static prop:Resources.SuperInt_Alert}"  HorizontalAlignment="Center" VerticalAlignment="Top"
               Grid.Row="1"
               Grid.Column="1"
               ></Image>

Gives me a System.Windows.Markup.XamlParseException with Message='Provide value on 'System.Windows.Markup.StaticExtension' threw an exception.'. The Xaml designer also doesn't show my image. It's blank, but no errors.

While

<Image Source="/Stark;component/Resources/SuperInt_Alert.png"  HorizontalAlignment="Center" VerticalAlignment="Top"
       Grid.Row="1"
       Grid.Column="1"
       ></Image>

Works just fine. It even shows it in my designer.

Why?

I'm just trying to understand the differences between the two ways to use a resource. What is wrong with the first way. I actually like the first way better, because when I type in the resource name, it lets me use IntelliSense to auto-complete my resource name. The second way does not because it is like a string.

Also, I do not have to set the resource type to Public, correct? I can leave it as internal because no other projects is using my resource file? I just have to make sure the build type is set to Resource/Content/Embedded Resource, correct?

Thanks so much!

CREW
  • 926
  • 3
  • 17
  • 32
  • 2
    What type is the `Resources.SuperInt_Alert` property? My guess is that WPF/XAML knows how to convert a string into an ImageSource but it doesn't know how to convert whatever that property is into an ImageSource, probably a System.Drawing.Image or something like that. – Mike Zboray Jul 31 '14 at 03:55
  • It was added using the Resource Designer by clicking 'Images' under resource type. I believe by looking at the resources.resx.cs code, it is 'internal static System.Drawing.Bitmap SuperInt_Alert' – CREW Jul 31 '14 at 03:57
  • Did my last comment help? Any other code I can supply to help narrow this down? – CREW Aug 01 '14 at 08:42
  • Check the inner exceptions of the error, is it something like "System.Drawing.Bitmap' is not a valid value for property 'Source'"? – Simon Mourier Aug 04 '14 at 07:06

1 Answers1

5

Yes, I certainly agree that this option: Source="{x:Static prop:Resources.SuperInt_Alert}" is better. And yes, you may and you should use it. And you correctly mentioned the cause of your problem. To be able to refer from XAML to property from resources you have to make them public - making them internal generates XamlParseException, you've mentioned. I am not 100% sure why it is but I assume this is because Xaml needs to be parsed. It is parsed by external assemblies. And to obtain proper value code from those assemblies needs to refer to Resources in your project, but they are internal, so normally not accessible outside of the assembly.

Additionally you will need to convert Bitmap object loaded from resources to ImageSource. To do this you may use Binding and apply there proper converter. Here is exemplary converter class:

public class BitmapToImageSourceConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        MemoryStream ms = new MemoryStream();
        ((System.Drawing.Bitmap)value).Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
        BitmapImage image = new BitmapImage();
        image.BeginInit();
        ms.Seek(0, SeekOrigin.Begin);
        image.StreamSource = ms;
        image.EndInit();

        return image;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

The above class is taken from http://www.shujaat.net/2010/08/wpf-images-from-project-resource.html You will need to add BitmapToImageSourceConverter to resources in your Window:

<Window.Resources>
    <my:BitmapToImageSourceConverter x:Key="imageSourceConverter"/>
</Window.Resources>

Here is how you you should define your Image to make use of the converter:

 <Image Source="{Binding Source={x:Static prop:Resources.Picture1}, Converter={StaticResource imageSourceConverter}}"/>
mr100
  • 4,340
  • 2
  • 26
  • 38
  • perfection! it addressed the conversion issues as well as the issue of the need of a public bitmap type over internal – CREW Aug 06 '14 at 23:54
  • One last question, this new method is my preferred way of doing so, but Ive been using the other way for so long. So I'm still not as familiar with binding. Do you know why the Binding method doesn't keep PNG transparency? If I change ImageFormat.Bmp to ImageFormat.Png, it works, but will it run into any issues since it's not a bitmap format? – CREW Aug 06 '14 at 23:58
  • As you mention this is not the problem with Binding, but the implementation of converter I provided. Binding is very flexible. You should not look at the above implementation of BitmapToImageSourceConverter as a Bible but rather as non-ideal example just to show that it works. You need to adjust it to your needs. If you deal with png images then certainly ImageFormat.Png will be better choice. And learn more about Binding - all the fun with WPF comes through it. – mr100 Aug 07 '14 at 14:49
  • 1
    Good answer, works very well! One question though: Why is the `MemoryStream` not enclosed in a `using` block? – oneManArmin Jul 24 '17 at 14:53
  • This is a perfectly valid question, thank you! In the example in my answer you cannot really Dispose the stream because it is meant to be used by BitmapImage object later on to load picture. If you Dispose the stream it won't work, unless you set CacheOption property of BitmapImage to OnLoad. Here is a question that covers that: https://stackoverflow.com/questions/2097152/creating-wpf-bitmapimage-from-memorystream-png-gif. – mr100 Jul 25 '17 at 06:26
  • On the other hand MemoryStream (to my knowledge at least) does not use any unmanaged resources and thus it is not important for it to call Dispose method - it merely blocks writing and reading from the stream. Dispose method is very important for classes like FileStream where you need to explicitly close the handler to a file. For MemoryStream Dispose method seems to be more a burdeen imposed by inheriting it from Stream class than actually implementing vital operation. – mr100 Jul 25 '17 at 06:28