How to display a Notification Bubble in Windows Mobile using .NETCF

Yesterday, I found myself using an old piece of code that I wrote ages ago. It’s something I’ve used every now and then for past few years. Since I myself find it useful, I might as well share it. All the code does is display a Notification Bubble in Windows Mobile. To do this you use the Notification class in the Microsoft.WindowsCE.Forms namespace. Even though the Notification class is very straight forward and easy to use, I created a helper class so that I only need to write one line of code for displaying a notification bubble: NotificationBubble.Show(2, “Caption”, “Text”);

/// <summary>
/// Used for displaying a notification bubble
/// </summary>
public static class NotificationBubble
{
    /// <summary>
    /// Displays a notification bubble
    /// </summary>
    /// <param name="duration">Duration in which the notification bubble is shown (in seconds)</param>
    /// <param name="caption">Caption</param>
    /// <param name="text">Body</param>
    public static void Show(int duration, string caption, string text)
    {
        var bubble = new Notification
        {
            InitialDuration = duration,
            Caption = caption,
            Text = text
        };
 
        bubble.BalloonChanged += OnBalloonChanged;
        bubble.Visible = true;
    }
 
    private static void OnBalloonChanged(object sender, BalloonChangedEventArgs e)
    {
        if (!e.Visible)
            ((Notification)sender).Dispose();
    }
}

Hope you found this helpful.

Multi-platform Mobile Development – Sending SMS

This is a task that pretty much every mobile device can do, or at least any mobile phone can do. In this short post on multi-platform mobile development I would like to demonstrate how to send an SMS or launch the SMS compose window in a mobile application.

I’m going to demonstrate how to use the messaging API’s of the following platforms:

  • Android
  • Windows Phone 7
  • Windows Mobile 5.0 (and higher) using .NET Compact Framework
  • Windows Mobile using the Platform SDK (Native code)

Android

There are 2 ways of sending SMS from an Android application: Launching the Compose SMS window; Through the SmsManager API. I figured that since this article is supposed to demonstrate as many ways as possible for sending SMS that I create a helper class containing methods that I think would be useful or at least convenient to have.

Here’s a SMS helper class for Android that I hope you would find useful.

package com.christianhelle.android.samples;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.telephony.SmsManager;
import android.widget.Toast;

/**
* Helper class for sending SMS messages
*
*
@author Christian Resma Helle
*/
public class Sms {
   
private final static String SENT_ACTION = "SENT";
   
private final static String DELIVERED_ACTION = "DELIVERED";
   
private Context context;
   
private PendingIntent sentIntent;
   
private PendingIntent deliveredIntent;

    /**
     * Creates an instance of the SMS class
     *
     *
@param context     Context that owns displayed notifications
     */
   
public Sms(Context context) {
       
this.context = context;
        registerForNotification
();
   
}

    private void registerForNotification() {
       
sentIntent = PendingIntent.getBroadcast(context, 0, new Intent(SENT_ACTION), 0);
        deliveredIntent = PendingIntent.getBroadcast
(context, 0, new Intent(DELIVERED_ACTION), 0);

        context.registerReceiver(messageSentReceiver, new IntentFilter(SENT_ACTION));
        context.registerReceiver
(messageDeliveredReceiver, new IntentFilter(DELIVERED_ACTION));
   
}
    
   
protected void finalize() throws Throwable {
       
context.unregisterReceiver(messageSentReceiver);
        context.unregisterReceiver
(messageDeliveredReceiver);
   
}

    /**
     * Opens the Compose SMS application with the recipient phone number displayed
     *
     *
@param phoneNumber     recipient phone number of the SMS
     */
   
public void composeMessage(String phoneNumber) {
       
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("sms:" + phoneNumber));
        context.startActivity
(intent);
   
}

    /**
     * Opens the Compose SMS application with the recipient phone number and message displayed
     *
     *
@param phoneNumber     recipient phone number of the SMS
     *
@param text             message body
     */
   
public void composeMessage(String phoneNumber, String text) {
       
Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.putExtra
("sms_body", text);
        intent.putExtra
("address", phoneNumber);
        intent.setType
("vnd.android-dir/mms-sms");
        context.startActivity
(intent);
   
}

    /**
     * Opens the Compose SMS application with the multiple recipient phone numbers and the message displayed
     *
     *
@param phoneNumber     recipient phone numbers of the SMS
     *
@param text             message body
     */
   
public void composeMessage(String[] phoneNumbers, String text) {
       
StringBuilder sb = new StringBuilder();
       
for (String string : phoneNumbers) {
           
sb.append(string);
            sb.append
(";");
       
}
       
composeMessage(sb.toString(), text);
   
}

    /**
     * Send an SMS to the specified number
     *
     *
@param phoneNumber     recipient phone number of the SMS
     *
@param text             message body
     */
   
public void sendMessage(String phoneNumber, String text) {
       
sendMessage(phoneNumber, text, false);
   
}

    /**
     * Send an SMS to the specified number and display a notification on the message status
     * if the notifyStatus parameter is set to
<b>true</b>
    
*
     *
@param phoneNumber     recipient phone number of the SMS
     *
@param text             message body
     *
@param notifyStatus     set to <b>true</b> to display a notification on the screen
     *                         if the message was sent and delivered properly, otherwise
<b>false</b>
    
*/
   
public void sendMessage(String phoneNumber, String text, boolean notifyStatus) {
       
SmsManager sms = SmsManager.getDefault();
       
if (notifyStatus) {
           
sms.sendTextMessage(phoneNumber, null, text, sentIntent, deliveredIntent);   
       
} else {
           
sms.sendTextMessage(phoneNumber, null, text, null, null);
       
}   
    }

    /**
     * Send an SMS to multiple recipients and display
     *
     *
@param phoneNumber     recipient phone number of the SMS
     *
@param text             message body
     */
   
public void sendMessage(String[] phoneNumbers, String text) {
       
sendMessage(phoneNumbers, text, false);
   
}

    /**
     * Send an SMS to multiple recipients and display a notification
     * on the message status if notifyStatus is set to
<b>true</b>
    
*
     *
@param phoneNumber     recipient phone number of the SMS
     *
@param text             message body
     *
@param notifyStatus     set to <b>true</b> to display a notification on the screen
     *                         if the message was sent and delivered properly, otherwise
<b>false</b>
    
*/
   
public void sendMessage(String[] phoneNumbers, String text, boolean notifyStatus) {
       
StringBuilder sb = new StringBuilder();
       
for (String string : phoneNumbers) {
           
sb.append(string);
            sb.append
(";");
       
}
       
sendMessage(sb.toString(), text, notifyStatus);
   
}

    private BroadcastReceiver messageSentReceiver = new BroadcastReceiver() {
       
@Override
       
public void onReceive(Context context, Intent intent) {
           
switch (getResultCode()) {
           
case Activity.RESULT_OK:
                Toast.makeText
(context, "SMS sent", Toast.LENGTH_SHORT).show();
               
break;
           
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
                Toast.makeText
(context, "Generic failure", Toast.LENGTH_SHORT).show();
               
break;
           
case SmsManager.RESULT_ERROR_NO_SERVICE:
                Toast.makeText
(context, "No service", Toast.LENGTH_SHORT).show();
               
break;
           
case SmsManager.RESULT_ERROR_NULL_PDU:
                Toast.makeText
(context, "Null PDU", Toast.LENGTH_SHORT).show();
               
break;
           
case SmsManager.RESULT_ERROR_RADIO_OFF:
                Toast.makeText
(context, "Radio off", Toast.LENGTH_SHORT).show();
               
break;
           
}
        }
    }
;

    private BroadcastReceiver messageDeliveredReceiver = new BroadcastReceiver() {
       
@Override
       
public void onReceive(Context context, Intent intent) {
           
switch (getResultCode()) {
           
case Activity.RESULT_OK:
                Toast.makeText
(context, "Message delivered", Toast.LENGTH_SHORT).show();
               
break;
           
case Activity.RESULT_CANCELED:
                Toast.makeText
(context, "Message not delivered", Toast.LENGTH_SHORT).show();
               
break;
           
}
        }
    }
;
}

Before your Android application can send SMS it needs the right permissions for it. Add the SEND_SMS permission your AndroidManifest.xml

<uses-permission android:name="android.permission.SEND_SMS"></uses-permission>

Here are some examples on how to use the SMS helper class defined above from within an Activity class:

Sms sms = new Sms(getApplicationContext());

// Send an SMS to the specified number
sms.sendMessage("+4512345678", "Multi-platform Mobile Development");

// Send an SMS to the specified number and display a notification on the message status
sms.sendMessage("+4512345678", "Multi-platform Mobile Development", true);

// Send an SMS to multiple recipients
sms.sendMessage(new String[] { "+4512345678", "+4598765432" }, "Multi-platform Mobile Development");       

// Send an SMS to multiple recipients and display a notification on the message status
sms.sendMessage(new String[] { "+4512345678", "+4598765432" }, "Multi-platform Mobile Development", true);

// Opens the Compose SMS application with the recipient phone number displayed
sms.composeMessage("+4512345678");

// Opens the Compose SMS application with the recipient phone number and message displayed
sms.composeMessage("+4512345678", "Multi-platform Mobile Development");

// Opens the Compose SMS application with the multiple recipient phone numbers and the message displayed
sms.composeMessage(new String[] { "+4512345678", "+4598765432" }, "Multi-platform Mobile Development");

Windows Phone 7

This platform unfortunately doesn’t provide as vast a API collection compared to Android and Windows Mobile. To send an SMS in Windows Phone 7, you will have to use the SMS Compose page in the built-in messaging application. To launch this we call the Show() method in SmsComposeTask.

Here’s how to use SmsComposeTask

SmsComposeTask launcher = new SmsComposeTask();
launcher.To = "+45 12 34 56 78";
launcher.Body = "Multi-platform Mobile Development";
launcher.Show();

Windows Mobile 5.0 (and higher) using .NET Compact Framework

Sending an SMS in this platform is just as easy as doing so in Windows Phone 7. Windows Mobile provides native API’s for the Short Messaging System, these methods are exposed as C type methods in a DLL called sms.dll. Aside from the SMS API, the platform also offers another API called the Messaging API (CE MAPI) for sending SMS, MMS, and Emails. Microsoft has provided managed wrappers for these and many other API’s to make the life of the managed code developer a lot easier.

To send an SMS in Windows Mobile 5.0 (and higher) we use the SmsMessage object. There are 2 ways of accomplishing this: Using the Send() method of the SmsMessage class; Sending the SMS using the Compose SMS application

Here’s a snippet on how to send SMS using the Send() method

SmsMessage sms = new SmsMessage("+45 12 34 56 78", "Multi-platform Mobile Development");
sms.Send();

Here’s a snippet on how to send SMS using the Compose SMS application

SmsMessage sms = new SmsMessage("+45 12 34 56 78", "Multi-platform Mobile Development");
MessagingApplication.DisplayComposeForm(sms);

The code above depends on 2 assemblies that must be referenced to the project:

  • Microsoft.WindowsMobile.dll
  • Microsoft.WindowsMobile.PocketOutlook.dll

It is also possible to P/Invoke the SMS API through sms.dll, but this requires a slightly more complicated solution. In the next section, I will demonstrate how use the SMS API in native code. This should give you an idea on how to use the SMS API if you would like to go try the P/Invoke approach.

Windows Mobile using the Platform SDK (Native code)

Probably not very relevant for most modern day managed code developers but just to demonstrate as many ways to send SMS in as many platforms as possible I’d like to show how to send SMS in native code using the Windows CE Short Message Service (SMS) API.

Here’s a sample C++ helper class for sending SMS using the Platform SDK

#include "stdafx.h"
#include "sms.h"
#include <string>
 
class SmsMessage 
{
private:
    std::wstring recipient;
    std::wstring message;
 
public:
    SmsMessage(const wchar_t* phoneNumber, const wchar_t* text) 
    {
        recipient = phoneNumber;
        message = text;
    }
 
    void Send() 
    {
        SMS_HANDLE smshHandle;
        HRESULT hr = SmsOpen(SMS_MSGTYPE_TEXT, SMS_MODE_SEND, &smshHandle, NULL);
        if (hr != S_OK)
            return;
 
        SMS_ADDRESS smsaDestination;
        memset (&smsaDestination, 0, sizeof (smsaDestination));
        smsaDestination.smsatAddressType = SMSAT_INTERNATIONAL;
        lstrcpy(smsaDestination.ptsAddress, recipient.c_str());
 
        TEXT_PROVIDER_SPECIFIC_DATA tpsd;
        tpsd.dwMessageOptions = PS_MESSAGE_OPTION_NONE;
        tpsd.psMessageClass = PS_MESSAGE_CLASS1;
        tpsd.psReplaceOption = PSRO_NONE;
 
        SMS_MESSAGE_ID smsmidMessageID = 0;
        hr = SmsSendMessage(smshHandle, 
                            NULL, 
                            &smsaDestination, 
                            NULL,
                            (PBYTE) message.c_str(), 
                            (message.length() + 1) * sizeof(wchar_t), 
                            (PBYTE) &tpsd, 
                            sizeof(TEXT_PROVIDER_SPECIFIC_DATA), 
                            SMSDE_OPTIMAL, 
                            SMS_OPTION_DELIVERY_NONE, 
                            &smsmidMessageID);
 
        SmsClose (smshHandle);
    }
};

The code above requires the that the project is linked with sms.lib, otherwise you won’t be able to build.

Here’s a snippet of how to use the SMS helper class defined above:

SmsMessage *sms = new SmsMessage(L"+14250010001", L"Multi-platform Mobile Development");
sms->Send();
delete sms;

For those who don’t know what +14250010001 is, this is the phone number of the Windows Mobile emulator. For testing SMS functionality on the emulator, you can use this phone number.

That’s it for now. I hope you found this article interesting.

Widcomm Bluetooth Pairing Prompt

Not so long ago I had a project that involved Bluetooth communication from a windows mobile device to some custom designed hardware. Everything went smoothly with only a few bumps during the scope of the project. When we had the applications on the field, certain users where running on devices that used the Widcomm Bluetooth Stack and that gave us a few headaches. For one, our system doesn’t use any security features in bluetooth hence it doesn’t not require pairing. Our security architecture is service based and checks for certain keys being passed back and forth to an online service. Very standard stuff. The problem we had with the devices running on a Widcomm Bluetooth stack was that the user was always prompted to pair even though the device did not require pairing. The application was not designed to handle user input aside from logging in/out and some diagnostic features. The application was designed to just run quietly in the background with the device tucked in the users pocket.

Since quite a few devices use the Widcomm Bluetooth Stack I needed a quick fix/hack to avoid the user having to pick up the device and click “Yes” on the security prompt to communicate with the device. The fix had to be as simple as possible and had to run without disrupting the user. My not so clean solution was to create a small application that does nothing but check if the Widcomm security pairing prompt app was running and if so send a keystroke event to simulate the user clicking on “Yes”.

The Widcomm security prompt app main window uses the class name Broadcom_BTWizard with the window name Bluetooth. I check if this window exists and send the F1 keyboard event to simulate clicking on the left hardware button on the device

Here’s a code snippet in C#

[DllImport("coredll.dll")]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
 
[DllImport("coredll")]
static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);
 
const int KEYEVENTF_KEYUP = 0x02;
const int KEYEVENTF_KEYDOWN = 0x00;
static bool running = true;
 
static void CloseBroadcomWindowWorker()
{
    while (running)
    {
        var hwnd = FindWindow("Broadcom_BTWizard", "Bluetooth");
        if (hwnd != IntPtr.Zero)
        {
            keybd_event((byte)Keys.F1, 0, KEYEVENTF_KEYDOWN, 0);
            keybd_event((byte)Keys.F1, 0, KEYEVENTF_KEYUP, 0);
        }
 
        Thread.Sleep(5000);
    }
}

This is of course not the best solution but if you have a similar problem then this might save you some time if your only requirement is to get it to work as soon as possible. Otherwise, I would suggest avoiding the Widcomm Bluetooth Stack and just go for devices that use the Microsoft Bluetooth Stack

How to retrieve a list of installed applications using .NETCF

In this short post I’d like to demonstrate how to retrieve a list of installed applications on a Windows Mobile device using OMA Client Provisioning. First you need to add a reference to the Microsoft.WindowsMobile.Configuration assembly. To retrieve the list we need to process a specific configuration (UnInstall Configuration Service Provider) using the ConfigurationManager.ProcessConfiguration method. The configuration is described in an XML and the response to this will also be described in XML

To query the device we process this configuration:

<wap-provisioningdoc>
  <characteristic-query type="UnInstall"/>
</wap-provisioningdoc>

The query above will only return installed application that can be uninstalled. The device would then respond with something like this:

<wap-provisioningdoc>
  <characteristic type="UnInstall">
    <characteristic type="Microsoft Application#2">
      <parm name="uninstall" value="0"/>
    </characteristic>
    <characteristic type="Microsoft Application#1">
      <parm name="uninstall" value="0"/>
    </characteristic>
    <characteristic type="Demo Home Screen">
      <parm name="uninstall" value="0"/>
    </characteristic>
  </characteristic>
</wap-provisioningdoc>

And here’s how to accomplish this task using .NETCF and C#

var doc = new XmlDocument();
doc.LoadXml(@"<wap-provisioningdoc><characteristic-query type=""UnInstall""/></wap-provisioningdoc>");
doc = ConfigurationManager.ProcessConfiguration(doc, true);
 
var nodes = doc.SelectNodes("wap-provisioningdoc/characteristic[@type='UnInstall']/characteristic/@type");
foreach (var node in nodes.Cast<XmlNode>())
{
    Trace.WriteLine(node.InnerText);
}

I hope you found this useful.

Enumerating Bluetooth Devices from .NETCF

I recently had a project where I needed to send data to Bluetooth devices. The client applications where to run in several platforms, currently only J2ME (Nokia phones) and Windows Mobile phones. Windows Mobile actually offers a pretty decent Bluetooth stack but not all devices use this. One of the devices I needed to use used the Widcomm stack. Luckily, there is an open source project called 32feet.NET which came in very handy for providing a layer over the 2 different stacks I use. The 32feet.NET library was also incredibly easy and fun to use.

In this article I’d like to demonstrate how to enumerate Bluetooth devices using .NETCF and the 32feet.NET library. The following code will work on both Microsoft and Widcomm Bluetooth stacks:

using System.Diagnostics;
using InTheHand.Net.Sockets;
 
namespace BluetoothSample
{
    static class Program
    {
        private static void Main()
        {
            BluetoothDeviceInfo[] devices;
            using (BluetoothClient sdp = new BluetoothClient())
                devices = sdp.DiscoverDevices();
 
            foreach (BluetoothDeviceInfo deviceInfo in devices)
            {
                Debug.WriteLine(string.Format("{0} ({1})",deviceInfo.DeviceName, deviceInfo.DeviceAddress));
            }
        }
    }
}

An interesting thing I had to consider for this project was the CPU architecture or endianness of the device I’m running on and the device I’m sending data to. I needed to reverse the byte order of the numeric data I sent and received.

Cropping an Image in .NETCF

I’ve seen some people ask how to crop an image in .NET Compact Framework in the community. Although the operation is pretty simple, I’ll share a code snippet anyway.

public static Image Crop(Image image, Rectangle bounds)
{
    Image newImage = new Bitmap(bounds.Width, bounds.Height);
 
    using (Graphics g = Graphics.FromImage(newImage))
        g.DrawImage(image, 
                    new Rectangle(0, 0, newImage.Width, newImage.Height), 
                    bounds, 
                    GraphicsUnit.Pixel);
 
    return newImage;
 
}

What the code above does is to create a new image and draw part of the source image specified in the bounds to the new image.

.NET Compact Framework 3.5 Data Driven Applications

I recently discovered a new book called .NET Compact Framework 3.5 Data Driven Applications. I’ve so far only read a few chapters but it seems like a promising book providing a lot of examples.

Here’s a downloadable free chapter :
http://www.packtpub.com/data-driven-applications-with-.net-compact-framework-3-5/book?utm_source=christian-helle.blogspot.com&utm_medium=bookrev&utm_content=blog&utm_campaign=mdb_003787

I plan to write a detailed review of the book once I’m done reading it

Themed Image Button in .NETCF

In this article I’d like to demonstrate how to create a themed image button control. I’ll be using the same techniques in my previous article, How to draw a textured rounded rectangle and Semi-Transparent Controls in .NETCF.

First, we need to define our theme. We get the start and end gradient colors from alpha blending the SystemColors.Highlight color and Color.White

class Theme
{
    public static Color AlphaBlend(Color value1, Color value2, int alpha)
    {
        int ialpha = 256 - alpha;
        return Color.FromArgb((value1.R * alpha) + (value2.R * ialpha) >> 8,
                              (value1.G * alpha) + (value2.G * ialpha) >> 8,
                              (value1.B * alpha) + (value2.B * ialpha) >> 8);
    }
 
    public static Color GradientLight
    {
        get
        {
            var color = AlphaBlend(SystemColors.Highlight, Color.White, 100);
            return AlphaBlend(Color.White, color, 50); ;
        }
    }
 
    public static Color GradientDark
    {
        get
        {
            var color = AlphaBlend(SystemColors.Highlight, Color.Black, 256);
            return AlphaBlend(Color.White, color, 50); ;
        }
    }
}

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, 
    int dwNumVertex, 
    GRADIENT_RECT[] pMesh, 
    int dwNumMesh, 
    int dwMode);

Let’s wrap the P/Invoke call to GradientFill in a nice method

const int GRADIENT_FILL_RECT_V = 0x00000001;
 
public static void GradientFill(
    this Graphics graphics,
    Rectangle rect,
    Color startColor,
    Color endColor)
{
    var tva = new TRIVERTEX[2];
    tva[0] = new TRIVERTEX(rect.X, rect.Y, startColor);
    tva[1] = new TRIVERTEX(rect.Right, rect.Bottom, endColor);
    var gra = new GRADIENT_RECT[] { new GRADIENT_RECT(0, 1) };
 
    var hdc = graphics.GetHdc();
    GradientFill(hdc, tva, tva.Length, gra, gra.Length, GRADIENT_FILL_RECT_V);
    graphics.ReleaseHdc(hdc);
}

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 IntPtr CreatePatternBrush(IntPtr HBITMAP);
 
[DllImport("coredll.dll")]
static extern bool RoundRect(
    IntPtr hdc, 
    int nLeftRect, 
    int nTopRect, 
    int nRightRect, 
    int nBottomRect, 
    int nWidth, 
    int nHeight);

We’ll draw a textured rounded rectangle with a gradient fill that uses the current theme’s Highlight color as the base color.

public static void DrawThemedGradientRectangle(
    this Graphics graphics,
    Pen border,
    Rectangle area,
    Size ellipseSize)
{
    using (var texture = new Bitmap(area.Right, area.Bottom))
    {
        using (var g = Graphics.FromImage(texture))
            GradientFill(g, area, Theme.GradientLight, Theme.GradientDark);
 
        FillRoundedTexturedRectangle(graphics, border, texture, area, ellipseSize);
    }
}
 
static IntPtr CreateGdiPen(Pen pen)
{
    var style = pen.DashStyle == DashStyle.Solid ? PS_SOLID : PS_DASH;
    return CreatePen(style, (int)pen.Width, GetColorRef(pen.Color));
}
 
public static void FillRoundedTexturedRectangle(
    this Graphics graphics,
    Pen border,
    Bitmap texture,
    Rectangle rect,
    Size ellipseSize)
{
    Point old = new Point();
 
    var hdc = graphics.GetHdc();
    var hpen = CreateGdiPen(border);
    var hbitmap = texture.GetHbitmap();
    var hbrush = CreatePatternBrush(hbitmap);
 
    SetBrushOrgEx(hdc, rect.Left, rect.Top, ref old);
    SelectObject(hdc, hpen);
    SelectObject(hdc, hbrush);
 
    RoundRect(hdc, rect.Left, rect.Top, rect.Right, rect.Bottom, ellipseSize.Width, ellipseSize.Height);
 
    SetBrushOrgEx(hdc, old.Y, old.X, ref old);
    DeleteObject(hpen);
    DeleteObject(hbrush);
 
    graphics.ReleaseHdc(hdc);
}

Let’s wrap all the code above as extension methods to the Graphics class and use them in our owner drawn button control. A button control is one of the easiest owner drawn controls to create. Let’s keep it as simple as possible and only have 2 states for our button: pressed and not pressed.

class ThemedImageButton : Control
{
    bool pushed = false;
    private Bitmap image;
    private Bitmap offScreen;
 
    public Bitmap Image
    {
        get { return image; }
        set
        {
            image = value;
            Invalidate();
        }
    }
 
    protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);
 
        if (offScreen != null)
        {
            offScreen.Dispose();
            offScreen = null;
        }
        offScreen = new Bitmap(ClientSize.Width, ClientSize.Height);
    }
 
    protected override void OnPaint(PaintEventArgs e)
    {
        if (offScreen == null)
            offScreen = new Bitmap(ClientSize.Width, ClientSize.Height);
 
        using (var attributes = new ImageAttributes())
        using (var g = Graphics.FromImage(offScreen))
        {
            if (pushed)
            {
                using (var pen = new Pen(SystemColors.Highlight))
                    g.DrawThemedGradientRectangle(pen, ClientRectangle, new Size(4, 4));
            }
            else
                g.Clear(Parent.BackColor);
 
            var textSize = g.MeasureString(Text, Font);
            var textArea = new RectangleF(
                (ClientSize.Width - textSize.Width) / 2,
                (ClientSize.Height - textSize.Height),
                textSize.Width,
                textSize.Height);
 
            if (Image != null)
            {
                var imageArea = new Rectangle(
                    (ClientSize.Width - Image.Width) / 2,
                    (ClientSize.Height - Image.Height) / 2,
                    Image.Width,
                    Image.Height);
 
                var key = Image.GetPixel(0, 0);
                attributes.SetColorKey(key, key);
 
                g.DrawImage(
                    Image,
                    imageArea,
                    0, 0, Image.Width, Image.Height,
                    GraphicsUnit.Pixel,
                    attributes);
            }
 
            using (var brush = new SolidBrush(ForeColor))
                g.DrawString(Text, Font, brush, textArea);
 
            if (pushed)
            {
                var key = offScreen.GetPixel(0, 0);
                attributes.SetColorKey(key, key);
            }
            else
                attributes.ClearColorKey();
 
            e.Graphics.DrawImage(
                offScreen,
                ClientRectangle,
                0, 0, offScreen.Width, offScreen.Height,
                GraphicsUnit.Pixel,
                attributes);
        }
    }
 
    protected override void OnPaintBackground(PaintEventArgs e)
    {
    }
 
    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 OnParentChanged(EventArgs e)
    {
        base.OnParentChanged(e);
        Invalidate();
    }
 
    protected override void OnTextChanged(EventArgs e)
    {
        base.OnTextChanged(e);
        Invalidate();
    }
}

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

How to enumerate files on a Windows CE based device from the Desktop

In this article I’d like to demonstrate how to enumerate files on a Windows CE based device from the desktop.

Listing the contents of a directly on a Windows CE from the desktop is something I found to be useful every now and then. It involves using the Remote API and ActiveSync / Windows Mobile Device Center

As usual this will involve a few P/Invokes:

[DllImport("rapi.dll", CharSet = CharSet.Unicode)]
static extern int CeRapiInit();
 
[DllImport("rapi.dll", CharSet = CharSet.Unicode)]
static extern int CeRapiUninit();
 
[DllImport("rapi.dll", CharSet = CharSet.Unicode)]
static extern IntPtr CeFindFirstFile(string lpFileName, ref CE_FIND_DATA lpFindFileData);
 
[DllImport("rapi.dll", CharSet = CharSet.Unicode)]
static extern bool CeFindNextFile(IntPtr hFindFile, ref CE_FIND_DATA lpFindFileData);
 
[DllImport("rapi.dll", CharSet = CharSet.Unicode)]
static extern bool CeFindClose(IntPtr hFindFile);

We also need the CE_FIND_DATA structure

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct CE_FIND_DATA
{
    public int dwFileAttributes;
    public FILETIME ftCreationTime;
    public FILETIME ftLastAccessTime;
    public FILETIME ftLastWriteTime;
    public int nFileSizeHigh;
    public int nFileSizeLow;
    public int dwOID;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
    public string cFileName;
};
 
[StructLayout(LayoutKind.Sequential)]
struct FILETIME
{
    public int dwLowDateTime;
    public int dwHighDateTime;
}

The CeRapiInit method has be always called before performing Remote API operations. Once done, the CeRapiUninit must be called. For listing the files in a directory, I use CeFindFirstFile, CeFindNextFile, and CeFindClose. How this works: If the file(s) exists CeFindFirstFile will return a valid handle that can be used for calling CeFindNextFile. After going through all the files CeFindNextFile will return false and a call to CeFindClose needs to be called.

public static string[] GetFiles(string remoteDirectory)
{
    try
    {
        CeRapiInit();
 
        var list = new List<string>();
        var findData = new CE_FIND_DATA();
        var hFindFile = CeFindFirstFile(remoteDirectory + "\*", ref findData);
 
        if (hFindFile != new IntPtr(-1))
        {
            try
            {
                do
                {
                    if (findData.dwFileAttributes != (int)FileAttributes.Directory)
                        list.Add(findData.cFileName);
                } while (CeFindNextFile(hFindFile, ref findData));
            }
            finally
            {
                CeFindClose(hFindFile);
            }
        }
 
        return list.ToArray();
    }
    finally
    {
        CeRapiUninit();
    }
}

I hope you found this useful.

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(@"windowsmsn.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