Wednesday, January 27, 2010

How to draw a textured rounded rectangle in .NETCF

I'd like to demonstrate how to draw patterned rounded rectangles by P/Invoking the GDI function RoundRect using a patterned brush instead of a solid brush. Let's create an extension method called FillRoundedTexturedRectangle to the Graphics class. With this method we can fill rectangles with an image. This image will be drawn as tiles to fill the rectangle bounds. This method can come in handy for drawing complex textures or patterns into a rectangle. With a little alpha blending and gradient fills, one can achieve a modern glass effect in the user interface

We create our brush with the CreatePatternBrush function instead of CreateSolidBrush as we did in my previous article on How to draw a rounded rectangle in .NETCF. We will mostly use P/Invoke for creating and releasing GDI objects.

const int PS_SOLID = 0;
const int PS_DASH = 1;
 
[DllImport("coredll.dll")]
static extern IntPtr CreatePen(int fnPenStyle, int nWidth, uint crColor);
 
[DllImport("coredll.dll")]
static extern int SetBrushOrgEx(IntPtr hdc, int nXOrg, int nYOrg, ref Point lppt);
 
[DllImport("coredll.dll")]
static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobject);
 
[DllImport("coredll.dll")]
static extern bool DeleteObject(IntPtr hObject);
 
[DllImport("coredll.dll")]
static extern IntPtr CreatePatternBrush(IntPtr hbmp);
 
[DllImport("coredll.dll")]
static extern bool RoundRect(
    IntPtr hdc, 
    int nLeftRect, 
    int nTopRect, 
    int nRightRect, 
    int nBottomRect, 
    int nWidth, 
    int nHeight);
 
static uint GetColorRef(Color value)
{
    return 0x00000000 | ((uint)value.B << 16) | ((uint)value.G << 8) | (uint)value.R;
}

Now we have our P/Invoke definitions in place we can neatly wrap all P/Invoke operations in a single function and let's call that FillRoundedTexturedRectangle()

public static void FillRoundedTexturedRectangle(
    this Graphics graphics,
    Pen border,
    Bitmap texture,
    Rectangle rectangle,
    Size ellipseSize)
{
    var lppt = new Point();
    var hdc = graphics.GetHdc();
    var style = border.DashStyle == DashStyle.Solid ? PS_SOLID : PS_DASH;
    var hpen = CreatePen(style, (int)border.Width, GetColorRef(border.Color));
    var hbrush = CreatePatternBrush(texture.GetHbitmap());
 
    try
    {
        SetBrushOrgEx(hdc, rectangle.Left, rectangle.Top, ref lppt);
        SelectObject(hdc, hpen);
        SelectObject(hdc, hbrush);
 
        RoundRect(hdc,
                  rectangle.Left,
                  rectangle.Top,
                  rectangle.Right,
                  rectangle.Bottom,
                  ellipseSize.Width,
                  ellipseSize.Height);
    }
    finally
    {
        SetBrushOrgEx(hdc, lppt.Y, lppt.X, ref lppt);
        DeleteObject(hpen);
        DeleteObject(hbrush);
 
        graphics.ReleaseHdc(hdc);
    }
}

To use this extension method you need to create a Bitmap and a Pen. The pen will be used to draw the rounded border, and the Bitmap will be used as a fill (tiled). Here's an example where "e" is an instance of PaintEventArgs

using (var pen = new Pen(SystemColors.Highlight, 5))
using (var texture = new Bitmap(@"\windows\msn.gif"))
    e.Graphics.FillRoundedTexturedRectangle(pen, 
                                            texture, 
                                            ClientRectangle, 
                                            new Size(8, 8));


I hope you found this useful. If you're interested in the full source code then you can grab it here

Monday, January 25, 2010

How to draw a rounded rectangle in .NETCF

In this short article I'd like to demonstrate how to draw rounded rectangles by P/Invoking the GDI function RoundRect. Let's create an extension method called FillRoundedRectangle to the Graphics class.

In order to use the function we need to create a few GDI objects: a Pen to draw the border, and a Brush to fill the rectangle. We will mostly use P/Invoke for creating and releasing GDI objects

const int PS_SOLID = 0;
const int PS_DASH = 1;
 
[DllImport("coredll.dll")]
static extern IntPtr CreatePen(int fnPenStyle, int nWidth, uint crColor);
 
[DllImport("coredll.dll")]
static extern int SetBrushOrgEx(IntPtr hdc, int nXOrg, int nYOrg, ref Point lppt);
 
[DllImport("coredll.dll")]
static extern IntPtr CreateSolidBrush(uint color);
 
[DllImport("coredll.dll")]
static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobject);
 
[DllImport("coredll.dll")]
static extern bool DeleteObject(IntPtr hgdiobject);
 
[DllImport("coredll.dll")]
static extern bool RoundRect(
    IntPtr hdc, 
    int nLeftRect, 
    int nTopRect, 
    int nRightRect, 
    int nBottomRect, 
    int nWidth, 
    int nHeight);

The CreateSolidBrush function in native code actually takes a COLORREF parameter, and the developer would normally use the RGB macro to create it. We need to translate that macro into a .NET function

static uint GetColorRef(Color value)
{
    return 0x00000000 | ((uint)value.B << 16) | ((uint)value.G << 8) | (uint)value.R;
}

Now we have our P/Invoke definitions in place we can neatly wrap all P/Invoke operations in a single function and let's call that FillRoundedRectangle()

public static void FillRoundedRectangle(
    this Graphics graphics,
    Pen border,
    Color color,
    Rectangle rectangle,
    Size ellipseSize)
{
    var lppt = new Point();
    var hdc = graphics.GetHdc();
    var style = border.DashStyle == DashStyle.Solid ? PS_SOLID : PS_DASH;
    var hpen = CreatePen(style, (int)border.Width, GetColorRef(border.Color));
    var hbrush = CreateSolidBrush(GetColorRef(color));
 
    try
    {
        SetBrushOrgEx(hdc, rectangle.Left, rectangle.Top, ref lppt);
        SelectObject(hdc, hpen);
        SelectObject(hdc, hbrush);
 
        RoundRect(hdc, 
                  rectangle.Left, 
                  rectangle.Top, 
                  rectangle.Right, 
                  rectangle.Bottom, 
                  ellipseSize.Width, 
                  ellipseSize.Height);
    }
    finally
    {
        SetBrushOrgEx(hdc, lppt.Y, lppt.X, ref lppt);
        DeleteObject(hpen);
        DeleteObject(hbrush);
 
        graphics.ReleaseHdc(hdc);
    }
}

Using this extension method should be straight forward, but here's an example where "e" is an instance of PaintEventArgs:

using (var pen = new Pen(Color.Blue))
    e.Graphics.FillRoundedRectangle(
        pen, 
        Color.LightBlue, 
        new Rectangle(10, 10, 100, 100), 
        new Size(16, 16));


I hope you found this useful. If you're interested in the full source code then you can grab it here

Sunday, January 24, 2010

Pocket News - Newsgroup reader for Windows Mobile

A few of you who know me probably have seen me from the Mobility community, MSDN Forums or the Newsgroups. I love helping out in the Newsgroups, I had so much fun doing so that I needed an application where I can do so while I'm on the go. Being the nerd that I am, my first reaction to this was that I needed to write a mobile newsgroup reader. I actually did before ever checking that a few of them already existed! Oh well, it was fun to write. I used the application a lot, especially while waiting in the airport, or for the train, or wherever that time feels like it stopped.

I decided to share this application to the public and publish it on CodePlex. If you're interested in the source code or if you want to contribute to the project then check it out at http://pocketnews.codeplex.com.

Unfortunately the application misses some basic features that any messaging application should have like a drafts folder. Maybe some day I can pull myself together to finish it up, or maybe some one out there who loves the Newsgroups just as much as I do would want to contribute some time and code :)

SQLCE Code Generator

Some time ago, I started a hobby project for generating data access layer code for SQL Compact Edition databases. I managed to get as far as creating a custom tool and generating a code behind data access layer file (currently only in C#) for every table in the database.

I'm having a bit of a hard time finding the time to work on the project so I published it on CodePlex. If you're interested in the source code or if you want to contribute to the project then check it out at http://sqlcecodegen.codeplex.com/

Wednesday, January 20, 2010

How to toggle the Wi-fi radio

I had a task to complete today where I was to create an application to toggle the Wi-Fi radio. I had two major requirements for this task; I was supposed to not spend more an hour on this and it must run on older devices running Pocket PC 2003 (or older)

This is what I came up with, 1 function (the entry point) and it uses only 3 power management API calls; GetDevicePower, DevicePowerNotify, and SetDevicePower

Basically I spent most of the time finding the device name for the wireless device. It seems to be pretty used for Intermec devices as I tested it on 3 different devices (or it could also be that only those 3 devices used the same device name)

Anyway, here's the code (works only for Intermec devices):

#include <windows.h>
#include <pm.h>
 
#define INTERMEC_WIFI_DEVICE    L"{98C5250D-C29A-4985-AE5F-AFE5367E5006}\\BCMCF1"
 
int _tmain(int argc, _TCHAR* argv[])
{
    CEDEVICE_POWER_STATE state;
    GetDevicePower(INTERMEC_WIFI_DEVICE, POWER_NAME, &state);
    CEDEVICE_POWER_STATE newState = (state == D0) ? D4 : D0;
 
    DevicePowerNotify(INTERMEC_WIFI_DEVICE, newState, POWER_NAME);
    SetDevicePower(INTERMEC_WIFI_DEVICE, POWER_NAME, newState);
}


Normally when I experiment with the platform SDK, I just create native console applications and test how the function works. Since my application was simple and didn't need a UI, I just shipped it in native code.

But for the sake of sharing knowledge I ported my tiny application to the .NET Compact Framework. Here's the code (works only for Intermec devices):

[DllImport("coredll.dll", SetLastError = true)]
static extern int DevicePowerNotify(string name, CEDEVICE_POWER_STATE state, int flags);
 
[DllImport("coredll.dll", SetLastError = true)]
static extern int SetDevicePower(string pvDevice, int dwDeviceFlags, CEDEVICE_POWER_STATE DeviceState);
 
[DllImport("coredll.dll", SetLastError = true)]
static extern int GetDevicePower(string pvDevice, int dwDeviceFlags, ref CEDEVICE_POWER_STATE pDeviceState);
 
enum CEDEVICE_POWER_STATE : int
{
    PwrDeviceUnspecified = -1,
    D0 = 0,
    D1 = 1,
    D2 = 2,
    D3 = 3,
    D4 = 4,
    PwrDeviceMaximum = 5
}
 
const int POWER_NAME = 0x00000001;
const string ADAPTER_NAME = "{98C5250D-C29A-4985-AE5F-AFE5367E5006}\\BCMCF1";
 
static void Main()
{
    CEDEVICE_POWER_STATE state = CEDEVICE_POWER_STATE.PwrDeviceUnspecified;
    GetDevicePower(ADAPTER_NAME, POWER_NAME, ref state);
    CEDEVICE_POWER_STATE newState = (state == CEDEVICE_POWER_STATE.D0)
        ? CEDEVICE_POWER_STATE.D4
        : CEDEVICE_POWER_STATE.D0;
 
    DevicePowerNotify(ADAPTER_NAME, newState, POWER_NAME);
    SetDevicePower(ADAPTER_NAME, POWER_NAME, newState);
}


There are smarter, better, and non-OEM specific ways to do this, both in native and managed code. In native code, one can use the wireless device functions (GetWirelessDevice, ChangeRadioState, FreeDeviceList) in ossvcs.dll as described in this article. And in managed code, one can take advantage of the OpenNETCF Smart Device Framework.

Here's an example of how to use the OpenNETCF.WindowsMobile namespace in the Smart Device Framework for toggling the state of wireless devices:

using System.Linq;
using OpenNETCF.WindowsMobile;
 
static class Program
{
    static void Main()
    {
        var wifi = from radio in Radios.GetRadios()
                   where radio.RadioType == RadioType.WiFi
                   select radio;
 
        foreach (var radio in wifi)
            radio.RadioState = (radio.RadioState == RadioState.On) ? RadioState.On : RadioState.Off;
    }
}


I hope you found this article useful

Tuesday, January 19, 2010

Semi-Transparent Controls in .NETCF

Some time ago, I wrote an article called Transparent Controls in .NETCF which describes how to accomplish transparency in the .NET Compact Framework. In this article I'd like to discuss how to control the opacity levels of transparent controls. This is made possible by alpha blending the parent controls background image with the child controls background color or image.

Here is an example of semi-transparent controls.



The screen shots above contain a semi-transparent PictureBox, Label, and Button controls. The button control is very Vista inspired, having 2 vertical fading gradient fills and then alpha blending it with the background image

Implementing Semi-Transparent Controls

The container or parent control will be the same code I used in my previous article.

Windows Mobile 5 and higher offers 2 ways of alpha blending, one through the function AlphaBlend (GDI) and through the Imaging API (COM). In this example let's use the AlphaBlend function.

To begin with we need to define the BLENDFUNCTION structure that we'll use as a parameter to AlphaBlend.

struct BLENDFUNCTION
{
    public byte BlendOp;
    public byte BlendFlags;
    public byte SourceConstantAlpha;
    public byte AlphaFormat;
}
 
enum BlendOperation : byte
{
    AC_SRC_OVER = 0x00
}
 
enum BlendFlags : byte
{
    Zero = 0x00
}
 
enum SourceConstantAlpha : byte
{
    Transparent = 0x00,
    Opaque = 0xFF
}
 
enum AlphaFormat : byte
{
    AC_SRC_ALPHA = 0x01
}


Next step is to define our P/Invoke declaration for AlphaBlend

[DllImport("coredll.dll")]
static extern bool AlphaBlend(
    IntPtr hdcDest,
    int xDest,
    int yDest,
    int cxDest,
    int cyDest,
    IntPtr hdcSrc,
    int xSrc,
    int ySrc,
    int cxSrc,
    int cySrc,
    BLENDFUNCTION blendfunction);


We'll be using the GradientFill method as well in this example. For this we need to define 2 structures, TRIVERTEX and GRADIENT_RECT

struct TRIVERTEX
{
    private int x;
    private int y;
    private ushort Red;
    private ushort Green;
    private ushort Blue;
    private ushort Alpha;
 
    public TRIVERTEX(int x, int y, Color color)
        : this(x, y, color.R, color.G, color.B, color.A)
    {
    }
 
    public TRIVERTEX(
        int x, int y,
        ushort red, ushort green, ushort blue,
        ushort alpha)
    {
        this.x = x;
        this.y = y;
        Red = (ushort)(red << 8);
        Green = (ushort)(green << 8);
        Blue = (ushort)(blue << 8);
        Alpha = (ushort)(alpha << 8);
    }
}
 
struct GRADIENT_RECT
{
    private uint UpperLeft;
    private uint LowerRight;
 
    public GRADIENT_RECT(uint ul, uint lr)
    {
        UpperLeft = ul;
        LowerRight = lr;
    }
}


Using the 2 structures above we can now define our P/Invoke for GradientFill

[DllImport("coredll.dll")]
static extern bool GradientFill(
    IntPtr hdc,
    TRIVERTEX[] pVertex,
    uint dwNumVertex,
    GRADIENT_RECT[] pMesh,
    uint dwNumMesh,
    uint dwMode);


And then lets wrap those neatly in some extension methods to the Graphics class

public static class GraphicsExtension
{
    public static void AlphaBlend(this Graphics graphics, Image image, byte opacity)
    {
        AlphaBlend(graphics, image, opacity, Point.Empty);
    }
 
    public static void AlphaBlend(this Graphics graphics, Image image, byte opacity, Point location)
    {
        using (var imageSurface = Graphics.FromImage(image))
        {
            var hdcDst = graphics.GetHdc();
            var hdcSrc = imageSurface.GetHdc();
 
            try
            {
                var blendFunction = new BLENDFUNCTION
                {
                    BlendOp = ((byte)BlendOperation.AC_SRC_OVER),
                    BlendFlags = ((byte)BlendFlags.Zero),
                    SourceConstantAlpha = opacity,
                    AlphaFormat = 0
                };
                AlphaBlend(
                    hdcDst,
                    location.X == 0 ? 0 : -location.X,
                    location.Y == 0 ? 0 : -location.Y,
                    image.Width,
                    image.Height,
                    hdcSrc,
                    0,
                    0,
                    image.Width,
                    image.Height,
                    blendFunction);
            }
            finally
            {
                graphics.ReleaseHdc(hdcDst);
                imageSurface.ReleaseHdc(hdcSrc);
            }
        }
    }
 
    public static void GradientFill(
        this Graphics graphics,
        Rectangle rectangle,
        Color startColor,
        Color endColor,
        GradientFillDirection direction)
    {
        var tva = new TRIVERTEX[2];
        tva[0] = new TRIVERTEX(rectangle.Right, rectangle.Bottom, endColor);
        tva[1] = new TRIVERTEX(rectangle.X, rectangle.Y, startColor);
        var gra = new[] { new GRADIENT_RECT(0, 1) };
 
        var hdc = graphics.GetHdc();
        try
        {
            GradientFill(
                hdc,
                tva,
                (uint)tva.Length,
                gra,
                (uint)gra.Length,
                (uint)direction);
        }
        finally
        {
            graphics.ReleaseHdc(hdc);
        }
    }
 
    public enum GradientFillDirection
    {
        Horizontal = 0x00000000,
        Vertical = 0x00000001
    }
}


Now that we can call the AlphaBlend API we can start designing a simple UI. Let's create a container to for the semi-transparent control that exposes its background image allowing the child control to alpha blend part of the background image. Before we create our alpha button control, let's take a quick review on How to implement transparency in .NETCF.

In my old article I expose the background image through an interface that a container control must implement, I called it IControlBackground

interface IControlBackground
{
    Image BackgroundImage { get; }
}


Now that it's possible to retrieve the parent controls background image through a child control we can create our alpha button control. This control will be the usual button control where we will use a boolean flag to store the pushed state of the control, this state is changed during mouse events. The control is painted using 2 gradient fills using shades of black and the text is drawn in the center. Since the entire control is painted in the OnPaint event I override OnPaintBackground with blank code.

class AlphaButton : Control
{
    private bool pushed;
 
    protected override void OnMouseDown(MouseEventArgs e)
    {
        base.OnMouseDown(e);
        pushed = true;
        Invalidate();
    }
 
    protected override void OnMouseUp(MouseEventArgs e)
    {
        base.OnMouseUp(e);
        pushed = false;
        Invalidate();
    }
 
    protected override void OnPaint(PaintEventArgs e)
    {
        using (var backdrop = new Bitmap(Width, Height))
        {
            using (var gxOff = Graphics.FromImage(backdrop))
            {
                var topRect = new Rectangle(0, 0, ClientSize.Width, ClientSize.Height / 2);
                var bottomRect = new Rectangle(0, topRect.Height, ClientSize.Width, ClientSize.Height / 2);
 
                if (!pushed)
                {
                    gxOff.GradientFill(
                        bottomRect,
                        Color.FromArgb(0, 0, 11),
                        Color.FromArgb(32, 32, 32),
                        GraphicsExtension.GradientFillDirection.Vertical);
 
                    gxOff.GradientFill(
                        topRect,
                        Color.FromArgb(176, 176, 176),
                        Color.FromArgb(32, 32, 32),
                        GraphicsExtension.GradientFillDirection.Vertical);
                }
                else
                {
                    gxOff.GradientFill(
                        topRect,
                        Color.FromArgb(0, 0, 11),
                        Color.FromArgb(32, 32, 32),
                        GraphicsExtension.GradientFillDirection.Vertical);
 
                    gxOff.GradientFill(
                        bottomRect,
                        Color.FromArgb(176, 176, 176),
                        Color.FromArgb(32, 32, 32),
                        GraphicsExtension.GradientFillDirection.Vertical);
                }
 
                using (var border = new Pen(Color.White))
                    gxOff.DrawRectangle(border, 0, 0, ClientSize.Width - 1, ClientSize.Height - 1);
 
                if (!string.IsNullOrEmpty(Text))
                {
                    var size = gxOff.MeasureString(Text, Font);
                    using (var text = new SolidBrush(Color.White))
                        gxOff.DrawString(
                            Text,
                            Font,
                            text,
                            (ClientSize.Width - size.Width) / 2,
                            (ClientSize.Height - size.Height) / 2);
                }
 
                try
                {
                    var bgOwner = Parent as IControlBackground;
                    if (bgOwner != null && bgOwner.BackgroundImage != null)
                        gxOff.AlphaBlend(bgOwner.BackgroundImage, 70, Location);
                }
                catch (MissingMethodException ex)
                {
                    throw new PlatformNotSupportedException(
                        "AlphaBlend is not a supported GDI feature on this device", 
                        ex);
                }
            }
 
            e.Graphics.DrawImage(backdrop, 0, 0);
        }
    }
 
    protected override void OnPaintBackground(PaintEventArgs e)
    {
    }
}


I implement the IControlBackground interface in a simple Form class and draw my background image on the Form. I can use this directly as a container or create inherited classes and re-use the code in several occasions.

class FormBase : Form, IControlBackground
{
    Bitmap background;
 
    public FormBase()
    {
        background = new Bitmap(
            Assembly.GetExecutingAssembly().GetManifestResourceStream(
            "SemiTransparentSample.background.jpg"));
    }
 
    protected override void OnPaint(PaintEventArgs e)
    {
        e.Graphics.DrawImage(background, 0, 0);
    }
 
    public Image BackgroundImage
    {
        get { return background; }
    }
}


To finish it up let's create a Form that will contain the alpha buttons.

class MainForm : FormBase
{
    public MainForm()
    {
        Controls.Add(new AlphaButton
        {
            Font = new Font("Arial", 16f, FontStyle.Bold),
            ForeColor = Color.White,
            Text = "Alpha Button 1",
            Bounds = new Rectangle(20, 20, 200, 50)
        });
        Controls.Add(new AlphaButton
        {
            Font = new Font("Arial", 16f, FontStyle.Bold),
            ForeColor = Color.White,
            Text = "Alpha Button 2",
            Bounds = new Rectangle(20, 90, 200, 50)
        });
        Controls.Add(new AlphaButton
        {
            Font = new Font("Arial", 16f, FontStyle.Bold),
            ForeColor = Color.White,
            Text = "Alpha Button 3",
            Bounds = new Rectangle(20, 160, 200, 50)
        });
        Controls.Add(new AlphaButton
        {
            Font = new Font("Arial", 16f, FontStyle.Bold),
            ForeColor = Color.White,
            Text = "Alpha Button 4",
            Bounds = new Rectangle(20, 230, 200, 50)
        });
    }
}


I hope you found this interesting and insightful. If you're interested in the Visual Studio solution then you can download it here.