Important:
This is retired content. This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This content may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.
4/7/2010

Alex Yakhnin, Microsoft Corporation

April 2009

Summary

Learn how to use the alpha blending and gradient drawing APIs that are available on the Windows Mobile® platform to create a compelling and attractive user experience.

Download Msdn.UI_code.zip .

Applies To

Windows Mobile 5.0 and later

Microsoft .NET Compact Framework 3.5

Introduction

Advances in hardware for Windows Mobile devices over the last few years have led to the appearance of devices that enable you to create attractive and functional user interfaces (UIs) with rich graphics and natural interfaces. The Windows Mobile platform is necessary for developers to deliver such creative solutions.

When you examine some attractive and functional UIs on mobile devices or even on the Windows Vista® desktop, you can see that most applications use the transparency and gradient fill techniques. The Windows Mobile platform provides several useful native APIs that can take advantage of these techniques.

You can call these APIs from the .NET Compact Framework 3.5, create a Graphicsextension class, and use a sample of the Framework to simplify creation of attractive UIs.

Drawing an Image with Transparency

When drawing an image with transparency, the most common approach is to draw an image or an icon with just one background color being transparent. This can easily be done in managed code by using the SetColorKey(Color, Color) method that is described in the .NET Compact Framework Developer Guide on MSDN®.

The second most common approach is when the whole image becomes transparent and looks as if you could see through it. This effect is achieved by using a technique called "alpha blending". Alpha blending is the process of combining a translucent foreground with a background color, thereby producing a new, blended color with the appearance of partial transparency. The Windows Mobile platform includes the native AlphaBlend function that can apply alpha blending over the whole source image. If an image contains an alpha channel defined when it was created, you can draw it with the Imaging API that is part of the Windows Mobile platform since Windows Mobile 5.0.

Chris Lorton, one of the members of the .NET Compact Framework team at Microsoft, describes a set of declarations for using the AlphaBlendfunction and ImagingAPI through P/Invoke in his blog post. The code from his sample has become a part of the PlatformAPIsclass that you will find in the solution that accompanies this article.

Displaying a Gradient Fill

Another way of spicing up your applications is by displaying a gradient fill. Gradient fill is the process that blends two or more colors together by gradually changing one into another. This could be easily achieved through the GradientFill function . The sample usage of this function is described in the MSDN article, How to Display a Gradient Fill . Parts of this code are reused in the GradientFillclass in Msdn.UI_code.zip .

Extending Graphics

Now when you have all the required pieces, you are ready to wrap them all together in a more useful package. You can use the new functionality of the .NET Framework 3.5, which is called the Extensionmethods. Extension methods let developers add new methods to the public contract of an existing Common Language Runtime (CLR) type. You can extend the System.Drawing.Graphicsclass. The following code example shows these two methods.

Copy Code
/// <summary>
/// Draws an image with transparency
/// </summary>
/// <param name="gx">Graphics to drawn on.</param>
/// <param name="image">Image to draw.</param>
/// <param name="transparency">Transparency
constant</param>
/// <param name="x">X location</param>
/// <param name="y">Y location</param>
 public static void DrawAlpha(this Graphics gx, Bitmap image, 
								 byte transparency, int x, int y)
 {
	using (Graphics gxSrc = Graphics.FromImage(image))
	{
		 IntPtr hdcDst = gx.GetHdc();
		 IntPtr hdcSrc = gxSrc.GetHdc();
		 BlendFunction blendFunction = new BlendFunction();
		 // Only supported blend operation
		 blendFunction.BlendOp =
(byte)BlendOperation.AC_SRC_OVER;
		 // Documentation says put 0 here
		 blendFunction.BlendFlags = (byte)BlendFlags.Zero;
		 // Constant alpha factor
		 blendFunction.SourceConstantAlpha = transparency;  
		 // Don't look for per pixel alpha
		 blendFunction.AlphaFormat = (byte)0; 
		 PlatformAPIs.AlphaBlend(hdcDst, x, y, image.Width, 
						image.Height, hdcSrc, 0, 0, image.Width, 
						image.Height, blendFunction);
		 // Required cleanup to GetHdc()
		 gx.ReleaseHdc(hdcDst);  
	 // Required cleanup to GetHdc()
		 gxSrc.ReleaseHdc(hdcSrc);
}
 }

/// <summary>
/// Fills the rectagle with gradient colors
/// </summary>
/// <param name="gx">Destination graphics</param>
/// <param name="rc">Desctination rectangle</param>
/// <param name="startColorValue">Starting color for
gradient</param>
/// <param name="endColorValue">End color for
gradient</param>
/// <param name="fillDirection">The direction of the
gradient</param>
public static void FillGradientRectangle(this Graphics gx, 
	Rectangle rc, Color startColorValue, Color endColorValue, 
									FillDirection fillDirection)
{ 
			GradientFill.Fill(
			gx,
			rc,
			startColorValue,
			endColorValue,
			fillDirection); 	
 }

As you can see, the DrawAlphaand FillGradientRectanglemethods extend the Graphicsclass and now can be easily used in your drawing code. These methods are included in the GraphicsExtensionclass incorporated into the sample solution that is part of a download package for this article. The following code example shows how to use these methods.

Copy Code
void Form1_Paint(object sender, PaintEventArgs e)
{  
	 Rectangle rectangle = new Rectangle(0, 0, 200, 100);
	 // Create temporary bitmap
	 Bitmap bitmap = new Bitmap(200, 100); 	 
	 // Create temporary graphics
	 Graphics tempGraphics = Graphics.FromImage(bitmap);
	 // Draw a red rectangle in the temp graphics object
	 using (Brush brush = new SolidBrush(Color.Red))
	 {
		tempGraphics.FillRectangle(brush, rectangle);
	 }

	 // Draw a gradient background on the form
	 e.Graphics.FillGradientRectangle(this.ClientRectangle, 
				Color.Blue, Color.LightBlue, 
					 FillDirection.TopToBottom);

	 // Draw temp bitmap with alpha transparency
	 e.Graphics.DrawAlpha(bitmap, 60, 20, 100);
 }

The previous code example paints a form with gradient colors and draws a red rectangle with alpha transparency on top. The following illustration shows the result.

The GraphicsExtensionclass also includes several other very useful methods that let you draw rounded rectangles which could be drawn with a gradient colors and drawn with transparency, such as FillGradientRectangle, DrawRoundedRectangleAlpha, and more. Feel free to explore the "goodies" in this class.

The following code example shows the usage of the DrawGradientRoundedRectanglemethod.

Copy Code
void Form1_Paint(object sender, PaintEventArgs e)
{

	 // Prepare rectangle
	 Rectangle rectangle = new Rectangle(20, 100, 200, 30);

	 // Draw a gradient background on the form
	 e.Graphics.FillGradientRectangle(this.ClientRectangle,
				Color.Blue, Color.LightBlue,  
							 FillDirection.TopToBottom);

	 e.Graphics.DrawGradientRoundedRectangle(rectangle, Color.Red, 
			Color.LightPink, Color.Red, new Size(6, 6));
 }

And the following illustration shows the result.

Drawing Rules

If you examine the previous code examples, you notice a certain rule for achieving transparency on a Windows Mobile platform: the drawing must be done in the same graphics context. This is dictated by the fact thatWindows Mobile does not support transparency on the windowing level. If you create a COM control deriving from the System.Windows.Forms.Controlclass, you will encounter difficulties when you try to make the control transparent in relation to the form that is hosting it.

Remember, too, that, even though the hardware is becoming more powerful, the memory and processor speeds are still somewhat limited compared with a desktop computer. Every allocation of the graphics context, brush, or a simple drawing operation takes time and resources. The most optimal way of drawing would be to use a single instance of the System.Drawing.Graphicsobject.

There is also a noticeable flickering when you run the sample. To resolve the flickering issue, you should use double-buffering technique which I explained in my older article .

Armed with this knowledge and the GraphicsExtensionclass described earlier, I have created a sample UI framework that you can use as a starting point when you develop your own applications for the .NET Compact Framework.

Starting from the UIElement Class

This framework builds on the following premises:

  • Every UI element should handle its own drawing.

  • The single Graphicsobject should be passed to the UI element to enable drawing.

Look at the UIElementclass from the solution. This class is a base class that is used by the inheritors to create elements like ImageElement, TextBlock, and Border. This class implements a set of events, properties and the following methods.

Copy Code
  /// <summary>
  /// Renders the UIElement.
  /// </summary>
  /// <param name="graphics">Graphics object.</param>
  public void Render(Graphics graphics)
  {
	 this.OnRender(graphics);
  }

  /// <summary>
  /// Render method to implement by inheritors
  /// </summary>
  /// <param name="graphics">Graphics object.</param>
  protected virtual void OnRender(Graphics graphics)
  {

  }

As you can see from this code, the Rendermethod can be called from the form or control with an instance of the Graphicsobject. Therefore, the virtual OnRendermethod would be called on any concrete class that implements it. The following code example shows how the ImageElementclass implements the OnRendermethod.

Copy Code
protected override void OnRender(Graphics graphics)
{
	 if (this.image != null)
	 {
		 if (transparentBackground)
		 {
			 graphics.DrawImageTransparent(this.image, new 
			 Rectangle(this.Left, this.Top, this.Width,
this.Height)); 				 
		 }
		 else if (alphaChannel)
		 { 			
			 graphics.DrawImageAlphaChannel(this.imagingImage,
												this.Left,
this.Top);
		 }
		 else
		 {
			 graphics.DrawAlpha(this.image, this.Opacity, 
											 this.Left,
this.Top);
		 }
	 }
 }

In this method, you are taking advantage of the methods from the GraphicsExtensionclass to draw an image with transparency or with the alpha channel. The following code example shows the same OnRendermethod in the TextBlockclass.

Copy Code
protected override void OnRender(Graphics drawingContext)
{
	if (this.Text != null)
	{
		Size sizeContent = Size.Empty;
		// Prepare font
		Font font = new Font(this.fontFamily, 
this.fontSize, fontStyle);
		// Measure the string
		SizeF sizeContentF =
drawingContext.MeasureString(this.Text,
 font);
		sizeContent.Width = (int)sizeContentF.Width;
		sizeContent.Height = (int)sizeContentF.Height;
		// Calculate the location of the text
		Point point =
GetLocationFromContentAlignment(this.alignment,
 sizeContent);
		// Draw the text
		using (Brush brushText = new SolidBrush(this.Foreground))
		{
			 Rectangle textRect = new Rectangle(point.X, point.Y,

this.Width - 1, this.Height - 1);
				drawingContext.DrawString(this.Text, font,
brushText, 
textRect);
		 }
		 // Clean up
		 font.Dispose();
}
}

Creating a Host

To use the UI elements from the framework, you have to have a valid form or control class to provide a Graphicsobject for the elements. The following code example shows the added UIFormclass, which inherits from the System.Windows.Formclass and overrides its OnPaintmethod.

Copy Code
public class UIForm : Form
{
private Canvas canvas;

public UIForm()
	{ 	
			this.canvas = new Canvas(this);
// Don't show title bar and allocate the full screen 
			this.FormBorderStyle = FormBorderStyle.None;
			this.WindowState = FormWindowState.Maximized;  
}
  
	protected override void OnPaint(PaintEventArgs e)
	{
		// Draw a background first
		this.offGx.Clear(this.BackColor);
		// Draw background image
		if (this.backgroundImage != null)
		{
			this.offGx.DrawImage(this.backgroundImage, 0, 0);
	}		 

		// Pass the graphics to the canvas to render
		if (this.canvas != null)
		{
			 this.canvas.Render(offGx);
	}

		// Blit the offscreen bitmap
		e.Graphics.DrawImage(offBitmap, 0, 0); 	 
	 }
}

From the previous code example, you can see that we are using the Canvasclass from the framework. This class plays the role of the parent to all UI elements by holding its instances in the VisualCollectionand relaying the Rendermethod call to each of the UI elements in the collection. The following code example shows what the Rendermethod looks like in the Canvasclass.

Copy Code
protected override void OnRender(Graphics graphics)
{
	 // Draw a back color
	 if (this.Background != null)
	 {
		 using (Brush brush = new SolidBrush(Background))
		 {
			graphics.FillRectangle(brush, new
Rectangle(this.Left, 
this.Top, this.Height, this.Width));
		 }
}

	// Pass the graphics objects to all UI elements
	for (int i = 0; i < children.Count; i++)
	{
		UIElement element = children[i];
		if (element.Visible)
		{
			element.Render(graphics);
	}
}
}

Custom Title and Menu Bar

Because we are making the UIFormto show full screen, you can implement a custom version of the title bar and menu bar on the form. You will find TitleBarand MenuBarclasses in the Msdn.UIFrameworkproject and declared in the UIForm. Both classes of course are derived from UIElementand implement its drawing in the Rendermethod. You can easily customize the way that they both look by changing the colors for the gradient or back color. Both TitleBarand MenuBarare exposed from UIFormby using appropriate properties.

Sample

In order to show how to use the UI framework, I have created a sample project that you will find in the Msdn.TestClientproject. It contains the DemoFormclass which is derived from UIForm. The following code example shows what the constructor of the DemoFormlooks like.

Copy Code
public DemoForm()
{
	 InitializeComponent();
	 // Set gradient for title bar
	this.TitleBar.Fill = new LinearGradient(Color.DarkBlue,
										Color.CornflowerBlue);
	this.TitleBar.Text = this.Text;
	 // Set values for menu
	 this.MenuBar.LeftMenu = "Exit";
	 this.MenuBar.RightMenu = "Location";

	 this.InitializeWeatherUI("75", "Redmond, WA", 50, 50);
	 this.InitializeSlideShow(50, 140, "Garden.jpg");
 }

In the constructor, we set a custom gradient to the TitleBarand some text values to the LeftMenuand RightMenuproperties for the MenuBar. Also, to demonstrate the capabilities of the UI framework, I have decided to reproduce the appearance of some Sidebar gadgets which are available in Windows Vista, Weather and Slide Show. I have "borrowed" some images that are included with the gadgets and included them as a part of the Msdn.TestClientproject in the Images folder. These images are created by using the alpha transparency and should be perfect to test the capabilities of the UI framework. This is why you see the InitializeWeatherUIand InitializeSlideShowmethods being called in the constructor code earlier. Look at one of the methods and see how it is done:

Copy Code
private void InitializeSlideShow(int left, int top, string
imageFile)
{
	 this.Canvas.AddElement<ImageElement>(() => new
ImageElement()
	 {
		Source = @"Images\slideshow_glass_frame.png",
		Top = top,
		Left = left,
		AlphaChannel = true,
	 });

	 this.Canvas.AddElement<ImageElement>(() => new
ImageElement()
	 {
		Name = "Slide",
		Source = @"Images\" + imageFile,
		Top = top + 5,
		Left = left + 4,
		Stretch = true,
		Width = 120,
		Height = 90
	 });
 }

As you can see, the InitializeSlideShowmethod accepts several parameters to set the position and an image file for the ImageElement.We add two ImageElementsto the Canvasby using the AddElementmethod. This method is declared the following way in the Canvasclass:

Copy Code
 
public T AddElement<T>(Func<T> buildFunc) where T :
UIElement
{
	if (buildFunc == null)
		 throw new ArgumentNullException("buildFunc");

	T instance = buildFunc();
	 this.children.Add(instance as UIElement); 	 
	 return instance;
}

As you can see, the AddElementmethod has a parameter of a Funcdelegate which accepts an instance of the UIElement. This is why you can use the Object initializers when they create an instance of the ImageElement.

In order to reproduce the Weather gadget appearance in the sample, you can use a set of ImageElementand some TextBlockclasses to display the temperature and a city.

Copy Code
private void InitializeWeatherUI(string temperature, 
string city, int left, int top)
{ 	 

	this.Canvas.AddElement<ImageElement>(() => new
ImageElement()
	{
		 Source = @"Images\BLUEDOCKED-base.png", 		 
		 Top = top,
		 Left = left, 
		 AlphaChannel = true,
});

	this.Canvas.AddElement<ImageElement>(() => new
ImageElement()
	{
		 Source = @"Images\docked_sun.png", 		
		 Top = top,
		 Left = left,
		 AlphaChannel = true,
});

	// Display temperature
	this.Canvas.AddElement<TextBlock>(() => new
TextBlock()
	{
		 Text = temperature + Convert.ToChar(176),
		 Top = top + 2,
		 Left = left + 97, 	
		 FontSize = 12f,
		 FontStyle = FontStyle.Bold
});

	// Display city
	this.Canvas.AddElement<TextBlock>(() => new
TextBlock()
	{
		 Text = city,
		 Top = top + 40,
		 Left = left + 40,
		 Width = 200, 		
	 }); 
  }

And finally, this is what the DemoFormlooks like when you execute the project.

Conclusion

In this article, I have shown how to extend the capabilities of the Graphicsclass in the .NET Compact Framework, by using some helpful drawing APIs that are available on the Windows Mobile platform. I have also described the UI framework that I built to demonstrate the capabilities of the Windows Mobile platform. However, this framework should not be considered production-ready code. Think of it as an example that you can extend and change to suit your own needs.

Author Bio

Alex Yakhnin is a Senior Mobility Consultant for the Mobility Global Practices group at Microsoft Corporation. Before his work with Microsoft, Alex was the architect and developer of small and large systems for many financial companies. He has written several MSDN articles, and for many years he has been a .NET Compact Framework/Device Application Development MVP and also a part of the OpenNETCF initiative.

See Also