# AlphaBlend a Windows Control

### Question

• I found a way to set transparent in the following sample by reshape the control
http://www.opennetcf.com/Default.aspx?tabid=130
It also helped me to set irregular shape control. My button can become any shape I like now.

However, I would like to know is there any method to make it support alpha blending too?
http://blogs.msdn.com/chrislorton/archive/2006/04/07/570649.aspx
I found a sample from the above link. However, it seems that alpha blending is only supported in graphic. What I like is to put a star shape image into a picture box and make it transparent (become a star shape picturebox by using the sample from OpenNetCF) and allow me to set alpha value for the remaining part of the image. So that I can see what is behind the picturebox.

Friday, May 4, 2007 8:28 AM

### Answers

• Hi Alex

There is a way of implementing what you want.

The key point is ho have the parent (usually a Form or Panel) paint the controls background before painting the image using the Win32 API  AlphaBlend.

Here is a small code snipped that shows how this may look like:

Code Snippet

/// <summary>
/// External paint control interface
/// </summary>
public interface IPaintControl
{

// have the foreground painted

void InvokePaint (PaintEventArgs e);

// have the background painted

void InvokePaintBackground (PaintEventArgs e);

}

// Form class implementing the IPaintControl Interface

public class Form : System.Windows.Forms.Form , IPaintControl

{

//other stuff...

//IPaintControl

public void InvokePaint (PaintEventArgs e)
{
this.OnPaint (e);
}

public void InvokePaintBackground (PaintEventArgs e)
{
this.OnPaintBackground (e);
}

}

public class PictureBox: System.Windows.Forms.Control

{

//Fields and properties required

// eg the Alpha property and other related stuff

// P-Invoke stuff for SetViewportOrgEx

[StructLayout (LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;

[DllImport ("coredll.dll", SetLastError = true)]
private static extern int SetViewportOrgEx (IntPtr hDC, int x, int y, out NativeMethods.POINT lpPoint);

//Called by the framework to paint the control

protected override void OnPaint (PaintEventArgs e)

{

//Step 1: have the background painted by the parent

IPaintControl parent = this.Parent as IPaintControl;
if (parent != null)

{

Point location = this.Location;

using (Region rgn = new Region (this.ClientRectangle))
{

Graphics gfx = e.Graphics;

IntPtr hDC = gfx.GetHdc ();

SetViewportOrgEx (hDC, -location.X, -location.Y, out oldOrigin);

gfx.Clip = rgn;
parent.InvokePaintBackground (e);

parent.InvokePaint (e);

SetViewportOrgEx (hDC, oldOrigin.x, oldOrigin.y, oldOrigin);
gfx.ResetClip ();

gfx.ReleaseHdc (hDC);
}

}

//Step 2:

// Paint the picture using the AlphaBlend API

...

}

}

I successfully used this approach to inmplement a transparent label and other controls that do require a 'transparent' background.

Hope this makes sens

Michael

Monday, May 7, 2007 4:16 PM
• Hi Alex,

I would suggest in your case that it may be easiest just to take the hit and draw everything in your OnPaint method of the form, especially if you want to do various effects such as highlighting objects when an item is dragged on top of it etc.

You can use the mouse events and a little bit of logic to determine when the user taps within a rectangle which is a given item which can be dragged. This is probably going to be the easiest way to obtain the transparency effects you are looking for.

An alternative I menteiond previously is to capture the mouse when the user clicks on a picture box and take a "screenshot" of the form. You can then blit this "screenshot" onto the screen (alpha blended with the object you are moving around). Once the user finishes dragging the object around you can restore the form to it's original state. However this technique won't deal with z-order issues or if you have animated elements on the form etc. This is why I think the easiest approach for you would be to manually draw your form.

To answer your question about transparent labels you may like to use a technique Alex Yaknin mentions on his blog http://blog.opennetcf.org/ayakhnin/PermaLink,guid,34221459-8db8-41ef-91c7-5514eade8fca.aspx.

Hope this helps,

Christopher Fairbairn

Wednesday, May 9, 2007 1:26 AM
• Hi Alex

The .NET CF hides the Paint events for most controls - this includes the PictureBox control. This is because these controls officially do not raise a paint event.

Luckily the PictureBox does raise this event. To handle the event you have to add the event handler manually.

Here is a simple sample form that draws a string ontop of a PictureBox:

Code Snippet

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

//Add the event handler for the hidden paint event

this.pictureBox1.Paint += new PaintEventHandler(pictureBox1_Paint);

}

void pictureBox1_Paint(object sender, PaintEventArgs e)

{

//draw some text on the image (as a sample)

using (SolidBrush brush = new SolidBrush (Color.Red))

{

using (StringFormat sf = new StringFormat ())

{

sf.Alignment = StringAlignment.Center;

sf.LineAlignment = StringAlignment.Center;

e.Graphics.DrawString("Just a Sample", this.Font, brush, this.pictureBox1.ClientRectangle, sf);

}

}

}

}

Hope this solves the issue

Michael

Wednesday, May 9, 2007 7:58 AM
• Hi Alex,

Try the following, I have no idea if it compiles (I don't have VB.NET support installed) but it should give you a starting point atleast.

Code Snippet
Public Class Form1
Inherits Form

Public Sub New()
InitializeComponent
AddHandler Me.pictureBox1.Paint, AddressOf pictureBox1_Paint
End Sub

Sub pictureBox1_Paint(ByVal sender As Object, ByVal e As PaintEventArgs)
' Using
Dim brush As SolidBrush = New SolidBrush(Color.Red)
Try
' Using
Dim sf As StringFormat = New StringFormat
Try
sf.Alignment = StringAlignment.Center
sf.LineAlignment = StringAlignment.Center
e.Graphics.DrawString("Just a Sample", Me.Font, brush, Me.pictureBox1.ClientRectangle, sf)
Finally
CType(sf, IDisposable).Dispose()
End Try
Finally
CType(brush, IDisposable).Dispose()
End Try
End Sub
End Class

For future reference I used the following online translation tool - http://www.developerfusion.co.uk/utilities/convertcsharptovb.aspx. If you search these forums you should be able to find posts which discuss various translation utilities for C# to VB.NEt and visa versa.

Hope it helps,

Christopher Fairbairn

Thursday, May 10, 2007 4:10 AM

### All replies

• No, you can't have the alpha blending on a control. The image that you want to alpha blend must be drawn on a top of another, not as a part of control.

Sunday, May 6, 2007 4:27 PM
• Hi,

Alex himself has actually just released a demo himself which may be of use to you. It's available on his blog at http://blog.opennetcf.org/ayakhnin/PermaLink,guid,0b5a9c02-0a44-4d5e-85f9-949cab4ad8f3.aspx and looks rather cool :-P However it's very similiar to the code you've mentioned in your original posting.

It makes use of a native API called AlphaBlend to perform alpha blending on a graphics context. This function is described here http://msdn2.microsoft.com/en-us/library/aa452850.aspx.

Can you describe in more detail what you are attempting to achieve by using alpha blending within a control. Although full alpha blending of windows is not available (as it is on Windows 2000+ on a desktop) there are various "puesdo alpha blending" approachs which can be taken to obtain On Screen Display (OSD) style text etc as seen on TV screens etc.

Perhaps you can simply paint your picture onto the form within a paint event handler for instance?

Hope it helps,

Christopher Fairbairn

Monday, May 7, 2007 4:24 AM
• Thanks. I want to use alpha in the control because it can be easier in coding. For example, I had a windows form, a few buttons, some label, etc. If I put an irregular shape button, picturebox or label which allow user to drag and drop it. I think it would be easier to put the image into a picturebox and handle the drag of picturebox rather than handle the drag of a graphic paint on the form. What I want is just trying to use as simple code as I can to provide a most vista like UI on pocket pc. So, I would like to see something like controls.alpha = X% if possible. I have read the first link you provided. I am looking for some way to support alpha in its "lock" button but the button is actually a picturebox if possible.
Monday, May 7, 2007 10:02 AM
• Hi Alex

There is a way of implementing what you want.

The key point is ho have the parent (usually a Form or Panel) paint the controls background before painting the image using the Win32 API  AlphaBlend.

Here is a small code snipped that shows how this may look like:

Code Snippet

/// <summary>
/// External paint control interface
/// </summary>
public interface IPaintControl
{

// have the foreground painted

void InvokePaint (PaintEventArgs e);

// have the background painted

void InvokePaintBackground (PaintEventArgs e);

}

// Form class implementing the IPaintControl Interface

public class Form : System.Windows.Forms.Form , IPaintControl

{

//other stuff...

//IPaintControl

public void InvokePaint (PaintEventArgs e)
{
this.OnPaint (e);
}

public void InvokePaintBackground (PaintEventArgs e)
{
this.OnPaintBackground (e);
}

}

public class PictureBox: System.Windows.Forms.Control

{

//Fields and properties required

// eg the Alpha property and other related stuff

// P-Invoke stuff for SetViewportOrgEx

[StructLayout (LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;

[DllImport ("coredll.dll", SetLastError = true)]
private static extern int SetViewportOrgEx (IntPtr hDC, int x, int y, out NativeMethods.POINT lpPoint);

//Called by the framework to paint the control

protected override void OnPaint (PaintEventArgs e)

{

//Step 1: have the background painted by the parent

IPaintControl parent = this.Parent as IPaintControl;
if (parent != null)

{

Point location = this.Location;

using (Region rgn = new Region (this.ClientRectangle))
{

Graphics gfx = e.Graphics;

IntPtr hDC = gfx.GetHdc ();

SetViewportOrgEx (hDC, -location.X, -location.Y, out oldOrigin);

gfx.Clip = rgn;
parent.InvokePaintBackground (e);

parent.InvokePaint (e);

SetViewportOrgEx (hDC, oldOrigin.x, oldOrigin.y, oldOrigin);
gfx.ResetClip ();

gfx.ReleaseHdc (hDC);
}

}

//Step 2:

// Paint the picture using the AlphaBlend API

...

}

}

I successfully used this approach to inmplement a transparent label and other controls that do require a 'transparent' background.

Hope this makes sens

Michael

Monday, May 7, 2007 4:16 PM
• Hi Michael,

Thanks for sharing that code snippet, that is definatly a neater solution than some of the other ones I have used in the past for peusdo transparency.

I had assumed Alex had wanted to also be able to see any child windows (controls) with a lower Z order than his transparent non rectangular window. For example if you place two buttons controls underneath your modified picture box control the sections of the buttons covered by the transparent picture box control will not be visible.

This was why I asked Alex what he was attempting to achieve with his transparent control. One solution is to make a slight modification to your code so that you essentially take a screen snapshot and use that as your background to alpha blend with rather than requesting the parent control to render within it's WM_PAINT handler. This only works if the section of your form which is covered by your transparent control isn't too dynamic in it's display (i.e. no updating text  or moving graphics etc).

Thanks,

Christopher Fairbairn

Tuesday, May 8, 2007 12:35 AM
• Thanks for the help. I can not understand the code..but I will try to convert it to VB. As what Christopher said, there are some other control on the same form. And I have to consider the Z order too.

On the form, there is a background image (not a plan color) which is static. And there are a few small picturebox which will be used to load some bitmap image dynamically. User is allowed to drag and drop those picturebox to a designed recycle bin icon or a shopping cart icon area. I would like the picturebox to support alpha (better to have transparent too) when the picturebox is dragging. And there are some dynamic text label (it is also a problem, without directly paint the text on the form, how can I make the label to be transparent?), a textbox for entering a name and a few oval shape button for "next", "back" and "enter". Wherever the user drag, the user should still able to to see everything on the form.

So, is there a way to take a snapshot of screen for alphablend?

Tuesday, May 8, 2007 3:20 AM
• Hi Alex,

I would suggest in your case that it may be easiest just to take the hit and draw everything in your OnPaint method of the form, especially if you want to do various effects such as highlighting objects when an item is dragged on top of it etc.

You can use the mouse events and a little bit of logic to determine when the user taps within a rectangle which is a given item which can be dragged. This is probably going to be the easiest way to obtain the transparency effects you are looking for.

An alternative I menteiond previously is to capture the mouse when the user clicks on a picture box and take a "screenshot" of the form. You can then blit this "screenshot" onto the screen (alpha blended with the object you are moving around). Once the user finishes dragging the object around you can restore the form to it's original state. However this technique won't deal with z-order issues or if you have animated elements on the form etc. This is why I think the easiest approach for you would be to manually draw your form.

To answer your question about transparent labels you may like to use a technique Alex Yaknin mentions on his blog http://blog.opennetcf.org/ayakhnin/PermaLink,guid,34221459-8db8-41ef-91c7-5514eade8fca.aspx.

Hope this helps,

Christopher Fairbairn

Wednesday, May 9, 2007 1:26 AM
• Thanks. Seems that paint everything on the form as a graphic is still the most suitable way to develop a complex user interface. I can't get the transparent label work but I understand what he is doing and built my own one. Actually, the transparent label is still a graphic on the form. (I can't find any OnPaint event for a picturebox) But he get the properties values from the invisible label. Anyway, thanks a lot for giving me so much information.
Wednesday, May 9, 2007 5:22 AM
• Hi Alex

The .NET CF hides the Paint events for most controls - this includes the PictureBox control. This is because these controls officially do not raise a paint event.

Luckily the PictureBox does raise this event. To handle the event you have to add the event handler manually.

Here is a simple sample form that draws a string ontop of a PictureBox:

Code Snippet

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

//Add the event handler for the hidden paint event

this.pictureBox1.Paint += new PaintEventHandler(pictureBox1_Paint);

}

void pictureBox1_Paint(object sender, PaintEventArgs e)

{

//draw some text on the image (as a sample)

using (SolidBrush brush = new SolidBrush (Color.Red))

{

using (StringFormat sf = new StringFormat ())

{

sf.Alignment = StringAlignment.Center;

sf.LineAlignment = StringAlignment.Center;

e.Graphics.DrawString("Just a Sample", this.Font, brush, this.pictureBox1.ClientRectangle, sf);

}

}

}

}

Hope this solves the issue

Michael

Wednesday, May 9, 2007 7:58 AM
• Thanks!! It works in C#. But I can't put it back in VB. Do you know how to convert it?
Thursday, May 10, 2007 3:57 AM
• Hi Alex,

Try the following, I have no idea if it compiles (I don't have VB.NET support installed) but it should give you a starting point atleast.

Code Snippet
Public Class Form1
Inherits Form

Public Sub New()
InitializeComponent
AddHandler Me.pictureBox1.Paint, AddressOf pictureBox1_Paint
End Sub

Sub pictureBox1_Paint(ByVal sender As Object, ByVal e As PaintEventArgs)
' Using
Dim brush As SolidBrush = New SolidBrush(Color.Red)
Try
' Using
Dim sf As StringFormat = New StringFormat
Try
sf.Alignment = StringAlignment.Center
sf.LineAlignment = StringAlignment.Center
e.Graphics.DrawString("Just a Sample", Me.Font, brush, Me.pictureBox1.ClientRectangle, sf)
Finally
CType(sf, IDisposable).Dispose()
End Try
Finally
CType(brush, IDisposable).Dispose()
End Try
End Sub
End Class

For future reference I used the following online translation tool - http://www.developerfusion.co.uk/utilities/convertcsharptovb.aspx. If you search these forums you should be able to find posts which discuss various translation utilities for C# to VB.NEt and visa versa.

Hope it helps,

Christopher Fairbairn

Thursday, May 10, 2007 4:10 AM
• Thanks a lot for all of your help. It can work!!
Thursday, May 10, 2007 6:24 AM
• I got another problem when I was trying the transparent text on the picturebox.

When I reload the picturebox (repaint), the screen will become flicker. I searched for it and found that I should use double buffer. But I just can't get it work. Is double buffer supported in CF?

Actually, I had another application, which is using 640*480 screen, with a full screen picturebox to load image dynamically. Without putting any other control on the picturebox, but just replace Picturebox1.Image = SomeImage in a period of time (let's say the image will be changed automatically in each second) , I can still see the image redraw slowly...How can I prevent this flickering
Thursday, May 10, 2007 8:24 AM
• Hi Alex,

Automatic double buffering (by simply using the SetStyle method to turn it on) is not supported on the Compact Framework. Your best solution is to create a Bitmap, and then use the Graphics.FromImage method to obtain a Graphics reference which you can use to draw onto the bitmap. Once you have done all your drawing you can assign the bitmap to your picture box.

When doing this amount of work, you may just find it easier to handle the Paint event for the form, or a panel.

Hope this helps,

Christopher Fairbairn

Friday, May 11, 2007 12:10 AM
• Hi Christopher,

I have tired the method you mention already. But the screen still flick even I only draw one bitmap.

Dim i As New bitmap(BitmapFilePath)
Dim b As New bitmap(width, height)
Dim g As Graphic = Graphics.FromImage(b)
g.DrawImage(i)
e.Graphic.DrawImage(b)

Still flick

Thanks for help
Alex

Friday, May 11, 2007 2:42 AM
• Hi Alex,

Can you provide any details on your image? For example what resolution does it have, and what is it's colour depth?

You may also like to search these forums - for instance http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=307243&SiteID=1. If all you are doing is drawing a single bitmap (as your code sample suggests) you shouldn't need to double buffer it. You should really only need to double buffer if you are doing complex drawing, such as drawing lots of rectangles and text at runtime.

Thanks,

Christopher Fairbairn

Friday, May 11, 2007 2:59 AM
• Hi Christopher,

The image resolution is 640 *480 (It is also the resolution of my device's screen). And it is a 24 bit color. I only need to draw some simple string on the image. You can think that I am doing something like screen saver on pocket pc. However, without drawing any string on the image, the screen still flick. Even I remove the Paint event and just simply call Picturebox.Image = New bitmap(Path), the flick still appear.

Alex

Friday, May 11, 2007 3:31 AM
• Hi Alex,

I think I understand your problem now and can offer a solution.

When a paint event occurs the OS/framework initially draws overtop of the control with a background brush. This will paint the entire control white typically.

It is after this background painting occurs that the control then calls your Paint event to deal with painting the contents of the control.

Your bitmaps take a while to load in from the filesystem, and any delay here will be time when the user can see the white background that has been painted.

So the solution is to try to hide or remove this delay.

To hide it you can override the OnPaintBackground method and simply do nothing (i.e. don't call the base class). This will mean when the OS asks your application to paint the background of your control you're application will simply do nothing, meaning the previous image will still be visible. Hence the delay during repainting is still there, but instead of a white rectangle the user will see their previous image (hence it is "hidden").

To remove the delay you could load your image(s) outside of the Paint event. if you are doing a screensaver type thing, perhaps your images could be loaded during startup. Then rather than create a new bitmap each time the paint event occurs, you can simply use the pre-loaded one (which will be a quick event). I have provided an example of this second approach below:

Code Snippet

Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms

Namespace DeviceApplication10

Public Class Form1
Inherits Form

Public Sub New()
InitializeComponent
End Sub
Private flag As Boolean = True

Private Sub pictureBox1_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim bmp As Bitmap = Nothing
flag = Not flag
If flag Then
bmp = New Bitmap("\Program Files\DeviceApplication10\foo.bmp")
Else
bmp = New Bitmap("\Program Files\DeviceApplication10\bar.bmp")
End If
pictureBox1.Image = bmp
End Sub
End Class
End Namespace

With this sample application (assuming you have two images called foo.bmp and bar.bmp in the correct folder) you should be able to toggle between both images without any flicker by tapping on the form. Just place a picturebox control on the form of a blank project.

I hope this helps resolve your problem,

Thanks,

Christopher Fairbairn

Friday, May 11, 2007 3:56 AM
• Hi Christopher,

Thanks a lot for helping me so fast. I tried what you said. But I still saw the image draw from the top to the bottom once the image is need to redraw in picturebox1.image = bmp. I am thinking it is caused by the hardware. Maybe the refresh rate of the device screen is too low?

Thanks a lot,
Alex
Friday, May 11, 2007 4:24 AM
• Hi Alex,

I'm not quite too sure what is going on. I have been able to test this with large images (larger than 2048 x 1536 at 32bits per pixel) and do not see the problem you are finding.

The only suggestion I can make is that you try converting your image. Try converting it into a 16bit image if possible, since this will be closer to what the LCD hardware will be expecting natively.

Hope this helps,

Christopher Fairbairn

Friday, May 11, 2007 4:44 AM
• Code Snippet

Public LoadedImage As Integer = 0
Public Image_1 As Image = New Bitmap(currAppPath & "\Sunset.jpg")
Public Image_2 As Image = New Bitmap(currAppPath & "\Winter.jpg")

Private Sub PictureBox1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PictureBox1.Click
If LoadedImage = 1 Then
PictureBox1.Image = Image_2
LoadedImage = 2
Else
PictureBox1.Image = Image_1
LoadedImage = 1
End If
End Sub

Here is code is am using. The picturebox, the two image(24bits) and my device screen are all in 640*480 (Landscape). I removed all mainmenu and the taskbar(Max. the form) from the form to make a full screen picture. Look very sample, but I saw the image loaded from the top of the screen to the bottom...

Friday, May 11, 2007 6:52 AM
• I even tried to put these image into two pictureboxs at design time. And only switch the picturebox at run time. Still see the problem....

I tried to reduce half of the width and height of the picturebox. I still saw the flick but it looks much better. Seems that the hardware need to take some time to redraw a 640*480 screen.
Friday, May 11, 2007 7:00 AM
• Greetingz,

I hope this is the right place for this reply. Am hoping you might be able to answer my question to do with alphblend.

I have used the following code to "blend" two pictures with the ouput displayed in one of them. This works great but

Anyway, as i have stated all appears to work, i have picturebox1 loaded with a picture with plenty of black on the alpha channel this is combined with and displayed in picturebox3 which before this code runs has a purple fill. Therefore picturebox3 displays a purple backgroound and the picture from picturebox1 without the black.

If i then try to display the picture in picturebox3 in picturebox2 i get a picturebox with a gray fill nothing else??

I'm trying to learn VB so am not very knowledgable so if anyone can explain how to accomplish this and why i cant display the picture i would be most grateful

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)

Private Type BLENDFUNCTION
BlendOp As Byte
BlendFlags As Byte
SourceConstantAlpha As Byte
AlphaFormat As Byte
End Type
' BlendOp:
Private Const AC_SRC_OVER = &H0
' AlphaFormat:
Private Const AC_SRC_ALPHA = &H1

Private Declare Function AlphaBlend Lib "MSIMG32.dll" ( _
ByVal hdcDest As Long, _
ByVal nXOriginDest As Long, _
ByVal nYOriginDest As Long, _
ByVal nWidthDest As Long, _
ByVal nHeightDest As Long, _
ByVal hdcSrc As Long, _
ByVal nXOriginSrc As Long, _
ByVal nYOriginSrc As Long, _
ByVal nWidthSrc As Long, _
ByVal nHeightSrc As Long, _
ByVal lBlendFunction As Long) As Long

Private Sub Form_Paint()

Dim lBlend As Long
Dim bf As BLENDFUNCTION

bf.BlendOp = AC_SRC_OVER
bf.BlendFlags = 0
bf.SourceConstantAlpha = 255
bf.AlphaFormat = AC_SRC_ALPHA
CopyMemory lBlend, bf, 4

AlphaBlend Picture3.hDC, 0, 0, _
Picture3.ScaleWidth \ Screen.TwipsPerPixelX, _
Picture3.ScaleHeight \ Screen.TwipsPerPixelY, _
Picture1.hDC, 0, 0, _
Picture1.ScaleWidth \ Screen.TwipsPerPixelX, _
Picture1.ScaleHeight \ Screen.TwipsPerPixelY, _
lBlend

Picture2.Picture = Picture3.Picture

End Sub

Wednesday, May 23, 2007 8:25 PM
• heh, I can use this only in Windows Application. In Device Application I don't have .Paint defined for components.
Friday, June 8, 2007 9:19 AM
• Michael,
I tried your code and it worked well except in the situation where the parent form overrides its own paint function:

protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
IntPtr hdc = g.GetHdc(); ( error is thrown here with a Value does not fall within expected range)
...
...
}

at Microsoft.AGL.Common.MISC.HandleAr(PAL_ERROR ar)
at System.Drawing.Graphics.GetHdc()
at TSGuider.frmMain.OnPaint(PaintEventArgs e)
at TSGuider.frmMain.InvokePaint(PaintEventArgs e)
at MobileCustomControls.TransparentLabel.OnPaint(PaintEventArgs e)
at System.Windows.Forms.Control.WnProc(WM wm, Int32 wParam, Int32 lParam)
at System.Windows.Forms.Control._InternalWnProc(WM wm, Int32 wParam, Int32 lParam)
at Microsoft.AGL.Forms.EVL.EnterMainLoop(IntPtr hwnMain)
at System.Windows.Forms.Application.Run(Form fm)
at TSGuider.Program.Main()

Thursday, April 23, 2009 3:25 PM
• Hi Alex

There is a way of implementing what you want.

The key point is ho have the parent (usually a Form or Panel) paint the controls background before painting the image using the Win32 API  AlphaBlend.

Here is a small code snipped that shows how this may look like:

Code Snippet

/// <summary>
/// External paint control interface
/// </summary>
public interface IPaintControl
{

// have the foreground painted

void InvokePaint (PaintEventArgs e);

// have the background painted

void InvokePaintBackground (PaintEventArgs e);

}

// Form class implementing the IPaintControl Interface

public class Form : System.Windows.Forms.Form , IPaintControl

{

//other stuff...

//IPaintControl

public void InvokePaint (PaintEventArgs e)
{
this.OnPaint (e);
}

public void InvokePaintBackground (PaintEventArgs e)
{
this.OnPaintBackground (e);
}

}

public class PictureBox: System.Windows.Forms.Control

{

//Fields and properties required

// eg the Alpha property and other related stuff

// P-Invoke stuff for SetViewportOrgEx

[StructLayout (LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;

[DllImport ("coredll.dll", SetLastError = true)]
private static extern int SetViewportOrgEx (IntPtr hDC, int x, int y, out NativeMethods.POINT lpPoint);

//Called by the framework to paint the control

protected override void OnPaint (PaintEventArgs e)

{

//Step 1: have the background painted by the parent

IPaintControl parent = this.Parent as IPaintControl;
if (parent != null)

{

Point location = this.Location;

using (Region rgn = new Region (this.ClientRectangle))
{

Graphics gfx = e.Graphics;

IntPtr hDC = gfx.GetHdc ();

SetViewportOrgEx (hDC, -location.X, -location.Y, out oldOrigin);

gfx.Clip = rgn;
parent.InvokePaintBackground (e);

parent.InvokePaint (e);

SetViewportOrgEx (hDC, oldOrigin.x, oldOrigin.y, oldOrigin);
gfx.ResetClip ();

gfx.ReleaseHdc (hDC);
}

}

//Step 2:

// Paint the picture using the AlphaBlend API

...

}

}

I successfully used this approach to inmplement a transparent label and other controls that do require a 'transparent' background.

Hope this makes sens

Michael

Hi, this code does not work for me. here is my implementation;
/*
* Variables
*/
private Graphics graphics;
private IntPtr hdc;
private Bitmap backBuffer;
private Graphics backBufferGfx;
private IntPtr backBufferHdc = IntPtr.Zero;

/*
* Interop Native Functions and structures
*/
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
}

[DllImport("coredll.dll", SetLastError = true)]
private static extern int SetViewportOrgEx(IntPtr hDC, int x, int y, out POINT lpPoint);

/*
* Constructor
*/
public CTor(...)
{   this.Bounds = new Rectangle(48, 74, 96, 134);
this.backBuffer = new Bitmap(Bounds.Width, Bounds.Height);
this.backBufferGfx = Graphics.FromImage(this.backBuffer);
this.backBufferHdc = this.backBufferGfx.GetHdc();
}

/*
* Paint Event Handler's
*/
protected override void OnPaint(PaintEventArgs e)
{
Repaint(e.Graphics);
}

protected override void OnPaintBackground(PaintEventArgs e)
{
/* no flickering */
}

/*
* Painter Methods
*/
public void Repaint()
{
if (InvokeRequired)
{
BeginInvoke(new Action(Repaint));
return;
}
Repaint(graphics);
}

private void Repaint(Graphics g)
{

IInvokePaintControl invokePaintCtl = this.Parent as IInvokePaintControl;
if (invokePaintCtl != null)
{
Point location = this.Location;
using (Region rgn = new Region(this.ClientRectangle))
{
POINT oldOrigin = new POINT();
IntPtr hdc = backBufferHdc;
SetViewportOrgEx(hdc, -location.X, -location.Y, out oldOrigin);
backBufferGfx.Clip = rgn;
invokePaintCtl.InvokePaint(backBufferGfx, hdc);
SetViewportOrgEx(hdc, oldOrigin.x, oldOrigin.y, out oldOrigin);
backBufferGfx.ResetClip();

iconsBmp.PaintPixmap(backBufferHdc, upDestRect, upSrcRect);
if (buttonPressed)
iconsBmp.PaintPixmap(backBufferHdc, dnDestRect, dnSrcRect);
if (animiationEnabled)
iconsBmp.PaintPixmap(backBufferHdc, earphoneDestRect, earphoneSrcRects[blinkIdx]);
else
iconsBmp.PaintPixmap(backBufferHdc, earphoneDestRect, earphoneSrcRects[2]);
}

g.DrawImage(backBuffer, 0, 0);
}
}


It is produce output at wrong position and size. The Bounds of this, Custom Control, is 48, 74, 96, 134. It is suppose to be at position 48, 74, and have WidthxHeight of 96x134 but instead it is drawn at 96, 148 with WidthxHeight of 48x60. What is going on here? iconsPixmap is wrapper around the Alphablend method you mentioned. It is different in that Graphics.GetHdc() is passed in to method rather than ALphablend doing -- this prevents many many errors that I dont quite understand.

Please help.
Tuesday, August 11, 2009 3:39 PM
• Michael,
I tried your code and it worked well except in the situation where the parent form overrides its own paint function:

protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
IntPtr hdc = g.GetHdc(); ( error is thrown here with a Value does not fall within expected range)
...
...
}

at Microsoft.AGL.Common.MISC.HandleAr(PAL_ERROR ar)
at System.Drawing.Graphics.GetHdc()
at TSGuider.frmMain.OnPaint(PaintEventArgs e)
at TSGuider.frmMain.InvokePaint(PaintEventArgs e)
at MobileCustomControls.TransparentLabel.OnPaint(PaintEventArgs e)
at System.Windows.Forms.Control.WnProc(WM wm, Int32 wParam, Int32 lParam)
at System.Windows.Forms.Control._InternalWnProc(WM wm, Int32 wParam, Int32 lParam)
at Microsoft.AGL.Forms.EVL.EnterMainLoop(IntPtr hwnMain)
at System.Windows.Forms.Application.Run(Form fm)
at TSGuider.Program.Main()

I am getting the same error were you able to resolve it? I think I fixed but by allocating e.Graphics.GetHdc()
only once and passing it to methods that use alphablend that then have it created and destroyed by but call to alphablend . if you resolved how have you do it?
Tuesday, August 11, 2009 4:03 PM
• Fixed, it was totally my fault. Simple error of not properly setting Boundsl.
Thursday, August 13, 2009 12:14 AM