A Moan About ImageBrush In WPF

I am working on a 3D article in WPF, and I wanted to use an ImageBrush but was having some issues with it, so I decided to split this out into a small test app.

I had the following setup

image

And I simply wanted to test this out by creating an ImageBrush that I could use to say change the Background of a Button. So I had the following code

   1:              //THIS DOESNT WORK UNLESS I UNCOMMENT THE CODE BELOW, 
   2:              //WHICH IS NOT EVEN RELATED
   3:              System.Windows.Media.ImageBrush brush =
   4:                  new System.Windows.Media.ImageBrush();
   5:              BitmapImage img = new BitmapImage(
   6:                  new Uri(@"images/image1.jpg", UriKind.RelativeOrAbsolute));
   7:              brush.ImageSource = img;
   8:              btnImage.Background = brush;

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }Now I thought this should work. Yet when I ran it I got

image

So I then tried to add an image to the code so we now had

   1:  <Window x:Class="ImageBrush.Window1"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      Title="Window1" Height="300" Width="300">
   5:      <StackPanel x:Name="sp1">
   6:          <Button x:Name="btnImage" Margin="10" Height="60"/>
   7:      </StackPanel>
   8:  </Window>

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
And code behind

   1:              //THIS DOESNT WORK UNLESS I UNCOMMENT THE CODE BELOW, 
   2:              //WHICH IS NOT EVEN RELATED
   3:              System.Windows.Media.ImageBrush brush =
   4:                  new System.Windows.Media.ImageBrush();
   5:              BitmapImage img = new BitmapImage(
   6:                  new Uri(@"images/image1.jpg", UriKind.RelativeOrAbsolute));
   7:              brush.ImageSource = img;
   8:              btnImage.Background = brush;
   9:  
  10:  
  11:              //BUT IF I UNCOMMENT THESE THE ABOVE CODE WORKS JUST FINE
  12:              Image img2 = new Image();
  13:              img2.Source = img;
  14:              sp1.Children.Add(img2);

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 

 

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }This seemed to work for me. But I didn’t want an Image, so that was out. I figured this has to be some trick that Image can do that ImageBrush cant. For ImageBrush I imagined that I needed to read from the Assembly resources. So I had a hunt about and found the  following code over at Tamir Khasons blog.

image

Which allows us to do things like

image

or for framework elements

image

All of which is way coo. But it still didn’t sit right with me, so I asked my mate Josh Smith about this and he simply suggested changing the Build Action of the image to “Content”. This did the trick, the spectacular results of which are shown here

image

Brilliant hey. But beware, when we set a Build Action to “Content”, You also lose the ability to localize that image. (Images built with a Resource build action are loaded via a ResourceManager, which means they can be substituted with images in localized satellite resource assemblies.)

From here on this blog will be using some information that has been the result of a chat with Ian Griffiths.

In fact it’s perfectly possible to use a compiled in resource in an ImageBrush, you just need a more specific URI, so we could then use a url like the following

image

Where we use a fully-qualified pack URI. I called my test application ImageBrushAsResource by the way – obviously you’d need to put your component’s name there in its place.

We can actually use a Relative path in XAML though for an Image such as

image

It’s using ImageSourceConverter to convert that text string into an ImageSource, and the process that uses isn’t quite the same as the process you’ve used in your code. The Xaml parser will pass the ImageSourceConverter a context object that offers an IUriContext service, letting it discover the base URI of the containing Xaml document, and it resolves the URI relative to that. So it corresponds to this:

image

This lets you use a relative URI, as in the original example. It converts it into a full URI using your Window’s base URI, which will be something like “pack://application:,,,/ImageBrush;component/window1.xaml” so it ends up resolving your relative URI to the same fully-qualified URI that my first example uses. But the advantage here is that you don’t need to hard code that into your codebehind. You just ask WPF what the base URI should be.

So by using either of these methods in code behind you are able to do without Tamir’s code.

  • Use Build Action of Resource, and use full Pack Uri
  • Use Build Action of Resource, and use BaseUriHelper

All in all a rather interesting post, I feel.

Advertisements

3 thoughts on “A Moan About ImageBrush In WPF

  1. Mike Pelton says:

    Hi – have just walked this whole sorry path myself – wish I’d found your post first! Many thanks to you and the ebullient Mr Griffiths!

  2. sacha says:

    Yeah Ian G, hes some dude for sure.

  3. have previously been reading your weblog to get a number of days. essentially fancy what you posted. btw i’m conducting a study about this area. do you transpire to understand some other good websites or possibly forums where I would possibly get more information? thanks an awful lot.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: