A Bitmap By Any Other Name...
I'm blogging to:
The Greencards
Weather and Water
As I typed the title to this post, I wondered just how many people over the years have butchered "Billy the Bard"'s line from Romeo & Juliet? I'm sure it's ridiculously high and here I go contributing to it. Sorry about that Bill, you can roll back over in your grave now...
In the spirit of interoperability, I thought it may be interesting to take a look at a scenario where you want to write a WPF application that uses resources that are entered and stored via the Resource Editor in Visual Studio.
Let's create a cheesy little application that shows off how to do this. First we need to create a WPF application using VS. Then we add some resources using the Resource Editor. To do that we just go over to our Solution Explorer in VS, expand the Properties node and double-click on 'Resources.resx'. This will launch the Resource Editor.
In the Resource Editor, let's add a string resource and an image resource. For the string, we will use some lyrics to one of my favorite Soul Coughing songs 'Screenwriters Blues'.
Since we are using these lyrics, let's use the image of the 'Ruby Vroom' CD that the song is on as our resource image.
Okay, now that we have our resources stored, let's work on the rest of the app. Let's go to our XAML and add a Button and a Label.
<
StackPanel Name="stackPanel1">
...
<Button Name="button1" Margin="0,10,0,0"/>
<Label Name="label1"/>
...
</StackPanel>
What we are going to do is use the string from our resources for the Label and use the image as the background of our button. Now using the string is perfectly straight forward since a string is a string. The bitmap however is a horse of a different color or it smells different or something. (Yeah, I know...I'm mixing my metaphors).
So let's go to the code behind to start doing our thing. We need to figure out where we are going to put our logic to set the label and the button. Usually you do this in some event handler in the code behind so we'll do that as well. However, what event should we choose?Most of the time you see examples using the Loaded event for the Window so that would be normal for us to use as well. BUT, I ain't gonna do that. Why? Well, it's because I want to be different and it's a good habit to get into. Let me explain this a bit. Many times in our code behind, we are doing things like adding controls to our visual tree in a WPF application. And if we use the Loaded event handler for doing this, we can sometimes get undesireable results. For example, if we are using a parent control such as a StackPanel and if we were to handle the Loaded event and put logic there to add controls, you would see that the Loaded event fires more than once due to the re-layout of the window after each control is added. This would result in having the controls added multiple times. If we instead use the SourceInitialized event, we don't have this problem because it is fired only once.
Okay, in our code behind let's handle the SourceInitialized event and put our code there. The first thing we want to do is get our resources.
// Get the resources
System.Drawing.Bitmap resourceBitmap = Properties.Resources.ruby;
String resourceString = Properties.Resources.String1;
Next let's do set our label's content to be the value in our string resource.
// Set the label's content to be our resource string
this.label1.Content = resourceString;
Now comes the part that's just a bit trickier...setting our button's background to our resource image. It's only tricky because the resource happens to be of type System.Drawing.Bitmap and we cannot set our button's background to a System.Drawing.Bitmap. To do this, we have to convert the bitmap to something that WPF understands. Luckly, there is a helper function that makes this a bit easier. The helper function is called 'CreateBitmapSourceFromHBitmap' which can be found in the System.Windows.Interop.Imaging namespace.
// Convert the System.Drawing.Bitmap to a WPF BitmapSource that we can use
IntPtr hBitmap = resourceBitmap.GetHbitmap();
System.Windows.Int32Rect rect = new Int32Rect(0, 0, resourceBitmap.Width, resourceBitmap.Height);
BitmapSource bmpSrc = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, rect, BitmapSizeOptions.FromEmptyOptions());
Let's take a quick look at the function signature for this function:
public static BitmapSource CreateBitmapSourceFromHBitmap (
IntPtr bitmap,
IntPtr palette,
Int32Rect sourceRect,
BitmapSizeOptions sizeOptions
)
The first parameter is a pointer to our original bitmap as an HBITMAP. In order to supply that we have to call the GetHBitmap() function on our System.Drawing.Bitmap object.
The next parameter is a pointer to the bitmap's palette. Now we have a Palette property on our original bitmap object, but we can't pass it to this function because the function wants the palette as an IntPtr. So to make this happen, you are going to have to write some magic code that will convert the color palette to an IntPtr. (Actually, I tried this and never quite got it right). But the good news is that since our bitmap does not have a specific palette, we can just pass in IntPtr.Zero (null).
Next is the source rectangle for our bitmap. Note that the function want this to be in the form of an Int32Rect and not a System.Drawing.Rectangle object.
I don't totally understand what the final parameter is all about, but I played around with different settings and managed to get this working so I didn't worry about it too much.
Okay, now that we have converted our bitmap to something that we can use, let's set the background of our button to the image and make sure the size of the button is the proper size.
// Set the background property of our button to the converted resource bitmap
this.button1.Background = new ImageBrush(bmpSrc);
this.button1.Height = resourceBitmap.Height;
this.button1.Width = resourceBitmap.Width;
Okay, now let's run the app and see what it looks like:
Okay, just what we wanted huh?
Comments
- Anonymous
March 17, 2006
The comment has been removed - Anonymous
March 30, 2006
Dude, I hear ya! - Anonymous
April 06, 2006
The comment has been removed - Anonymous
April 06, 2006
Sweet! Glad to know I actually helped someone! I'm always up for a beer...
MHender