Friday, March 03, 2006

One of my colleagues recently purchased the Treo 700w and fall in love with it. Being a developer he came up with the idea to try to use the button on the phone's headphones to initiate the Voice Commander. So he approached me with the question on how to place a hook in the WM device in order to listen for all hardware button presses. As a matter of fact, Windows CE does support a keyboard hook through the SetWindowsHookEx API. This API requires the usage of the native callback, which was not possible in the first version of the CF. The current version of .NetCF empowered us with this very useful capability. So what are hooks? Put shortly, a hook is a function that you can create as a part of your application in order to monitor on the messages inside the operating system. Hooks were provided by Microsoft primarily to help developers with the debugging their applications. The incorrect usage of them very easily could bring the system down. Therefore the hooks should be a last resort when all other a more traditional methods don't satisfy your requirents. Below you will find the class HookKeys that wraps all appropriate API calls and exposes all catched messages as a managed HookEvent:

public class HookKeys

{

        #region delegates

 

        public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);

        public delegate void HookEventHandler(HookEventArgs e, KeyBoardInfo keyBoardInfo);

 

        public HookEventHandler HookEvent;

 

        #endregion

 

        #region fields

 

        private HookProc hookDeleg;

        private static int hHook = 0;

 

        #endregion

 

 

        public HookKeys()

        {

 

        }

 

        #region public methods

        ///

        /// Starts the hook

        ///

        public void Start()

        {

            if (hHook != 0)

            {

                //Unhook the previouse one

                this.Stop();

            }

 

            hookDeleg = new HookProc(HookProcedure);

            hHook = SetWindowsHookEx(WH_KEYBOARD_LL, hookDeleg, GetModuleHandle(null), 0);

 

            if (hHook == 0)

            {

                throw new SystemException("Failed acquiring of the hook.");

            }

        }

 

        ///

        /// Stops the hook

        ///

        public void Stop()

        {

            UnhookWindowsHookEx(hHook);

        }

 

        #endregion

 

        #region protected and private methods

 

        protected virtual void OnHookEvent(HookEventArgs hookArgs, KeyBoardInfo keyBoardInfo)

        {

            if (HookEvent != null)

            {

                HookEvent(hookArgs, keyBoardInfo);

            }

        }

     

 

        private int HookProcedure(int code, IntPtr wParam, IntPtr lParam)

        {

           KBDLLHOOKSTRUCT hookStruct =  (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));

 

           if (code < 0)

                return CallNextHookEx(hookDeleg, code, wParam, lParam);

 

           // Let clients determine what to do

           HookEventArgs e = new HookEventArgs();

           e.Code = code;

           e.wParam = wParam;

           e.lParam = lParam;

 

           KeyBoardInfo keyInfo = new KeyBoardInfo();

           keyInfo.vkCode = hookStruct.vkCode;

           keyInfo.scanCode = hookStruct.scanCode;

           OnHookEvent(e, keyInfo);

 

           // Yield to the next hook in the chain

           return CallNextHookEx(hookDeleg, code, wParam, lParam);

 

       }

 

        #endregion

 

       #region P/Invoke declarations

 

       [DllImport("coredll.dll")]

       private static extern int SetWindowsHookEx(int type, HookProc hookProc, IntPtr hInstance, int m);

 

       [DllImport("coredll.dll")]

       private static extern IntPtr GetModuleHandle(string mod);

 

       [DllImport("coredll.dll")]

       private static extern int CallNextHookEx(

               HookProc hhk,

               int nCode,

               IntPtr wParam,

               IntPtr lParam

               );

 

       [DllImport("coredll.dll")]

       private static extern int GetCurrentThreadId();

 

       [DllImport("coredll.dll", SetLastError = true)]

       private static extern int UnhookWindowsHookEx(int idHook);

 

       private struct KBDLLHOOKSTRUCT

       {

           public int vkCode;

           public int scanCode;

           public int flags;

           public int time;

           public IntPtr dwExtraInfo;

       }

 

       const int WH_KEYBOARD_LL = 20;

 

       #endregion

   }

 

        #region event arguments

  

    public class HookEventArgs : EventArgs

    {

        public int Code;    // Hook code

        public IntPtr wParam;   // WPARAM argument

        public IntPtr lParam;   // LPARAM argument

    }

 

    public class KeyBoardInfo

    {

        public int vkCode;

        public int scanCode;

        public int flags;

        public int time;

    }

 

        #endregion

 

 

In order to use this class in your program, just declare the varialble and hook up into HookEvent:

 

HookKeys hook = new HookKeys();

hook.HookEvent += new HookKeys.HookEventHandler(HookEvent);  

The HookKeys class should work properly, but there is something missing from this class to be complete. I am not going to tell you what it is. This is going to be a self learning quiz for my readers. You can post the anwers in the comments. I'll anounce the winners in the next post.

3/3/2006 10:32:08 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, January 17, 2006

The DateTimePicker control in the .NetCF v2 doesn't implement DropDown and CloseUp events which could be really helpful whe dealing with the picker. It doesn't mean that these events or messages are not available in the native counterpart. Win CE SDK docs show the presence of the DTN_CLOSEUP and DTN_DROPDOWN notification messages. It means that they are sent by the DateTime picker to its parent. So, I pulled the NativeWindow class to whip up the DateTimePickerEntender class that suclasses the form, catches the messages and raises the events to the client:

public class DateTimePickerExtender : NativeWindow

{

        public event System.EventHandler CloseUp;

        public event System.EventHandler DropDown;

 

        private IntPtr handle;

 

        public DateTimePickerExtender(IntPtr handle)

        {

            this.handle = handle;

            // Subclass the window

            this.AssignHandle(handle);

        }

 

        protected override void WndProc(ref Message m)

        {

            // Check for notification messages

            if (m.Msg == WM_NOTIFY)

            {

                NMHDR nmhdr = new NMHDR();

                        Marshal.PtrToStructure(m.LParam, nmhdr);

                switch (nmhdr.code)

                {

                    case DTN_DROPDOWN:       

                        // Raise the event

                        if (DropDown != null)

                            DropDown(this, new EventArgs());

                        break;

                    case DTN_CLOSEUP:

                        // Raise the event

                        if (CloseUp != null)

                            CloseUp(this, new EventArgs());

                        break;

                }

            }

            base.WndProc(ref m);

        }

 

 

        #region P/Invoke declarations

 

        private const int WM_NOTIFY = 0x004E;

 

        private const int DTN_FIRST = -760;

        private const int DTN_DROPDOWN = (DTN_FIRST + 6);

        private const int DTN_CLOSEUP = (DTN_FIRST + 7);

 

        private class NMHDR

        {

            public IntPtr hwndFrom = IntPtr.Zero;

            public uint idFrom = 0;

            public int code = 0; //uint

        }

 

        #endregion

}

Drop the DateTimePicker controls on the form, create and instance of the DateTimePickerExtender and hook up into the events:

DateTimePickerExtender dtEntender;

 

public Form1()

{

     InitializeComponent();

     dtEntender = new DateTimePickerExtender(this.Handle);

     dtEntender.CloseUp += new EventHandler(dtEntender_CloseUp);

     dtEntender.DropDown += new EventHandler(dtEntender_DropDown);

}

 

void dtEntender_DropDown(object sender, EventArgs e)

{

     Console.WriteLine("DropDown event");

}

 

void dtEntender_CloseUp(object sender, EventArgs e)

{

     Console.WriteLine("CloseUp event");

}

 

1/17/2006 7:35:53 PM (GMT Standard Time, UTC+00:00)  #     | 
 Thursday, January 05, 2006

Questions about embedding Macromedia Flash in CF application pop up from time to time on the CF NG. In fact it is very easy to do with Flash ActiveX for Pocket PC and WebBrowser control.

Here are five steps to achieve that:

1. Download and install Flash ActiveX for Pocket PC on your Pocket PC.

2. Create Smart Device and CF v2 project in VS 2005 and drop WebBrowser on your form.

3. Add flash file (.SWF) of your choice to your project and set its Built Action to content and “Copy if newer”

4. Create html file and make sure that you have a full path to the .SWF file in the src tag value:

<html>

<head>

<title>Dogshow Gametitle>

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

head>

 

<body bgcolor="#FFFFFF" margintop=0 topmargin=0 leftmargin=0 marginleft=0>

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=5,0,0,0" width="580" height="360">

  <param name=movie value="dogshow.swf">

  <param name=quality value=high>

  <embed src="file://\Program Files\MMediaClient\dogshow.swf" quality=high pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash" type="application/x-shockwave-flash" width="580" height="360">

  embed>

 

object>

body>

html>

5. Make a call to the Navigate method with the name of your html file:

webBrowser1.Navigate(new Uri(@“file://\Program Files\MMediaClient\dogshow.htm“));

 

And enjoy the show:

1/5/2006 4:56:57 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, January 03, 2006

You probably know how difficult it was to create a screen snapshot in CF v1. It required usage of many API calls and custom marshaling of a few structures. Just take a look at these resources:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/scibf.asp?frame=true

http://vault.netcf.tv/VaultService/VaultWeb/Blame.aspx?repid=2&path=%24%2fSDF%2fv1.4%2fOpenNETCF.Drawing%2fGDIPlus.cs&version=5&includedversions=20

The world has become a much better place since release of the CF v2 - availability of the native handles and ability to save image from Bitmap - have reduced the required code to just a few lines and one API call:

private void Snapshot(string filename, Graphics gx, Rectangle rect)

{

     Bitmap bmp = new Bitmap(rect.Width, rect.Height);

     // Create compatible graphics

     Graphics gxComp = Graphics.FromImage(bmp);

     // Blit the image data

     BitBlt(gxComp.GetHdc(), 0, 0, rect.Width, rect.Height, gx.GetHdc(), rect.Left, rect.Top, SRCCOPY);

     bmp.Save(filename, System.Drawing.Imaging.ImageFormat.Bmp);

     // Cleanup

     bmp.Dispose();

     gxComp.Dispose();

}

 

// P/Invoke declaration

[DllImport("coredll.dll")]

public static extern int BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);

 

const int SRCCOPY = 0x00CC0020;

All you need to use it is just place the Snapshot method on your form and call it like that:

//Create a snapshot of a current view of the TextBox

Snapshot(@"\Temp\save.bmp", this.CreateGraphics(), textBox1.Bounds);

 

1/3/2006 1:44:08 PM (GMT Standard Time, UTC+00:00)  #     | 
 Friday, December 09, 2005

I've got the following info from Microsoft guys that could be helpful for anybody:

The following downloads for SQL Server 2005 Mobile Edition (SQL Server Mobile) available at http://download.microsoft.com is described below. For more information please refer to the SQL Server Mobile website at http://www.microsoft.com/sql/editions/sqlmobile.

a.    Microsoft SQL Server 2005 Mobile Edition Server Tools - The package installs the SQL Server Mobile Replications Components on the machine running the IIS server. The components are required for connecting the SQL Server Mobile database on a mobile device to SQL Server 2000 SP3a database and later versions. Link to download: http://www.microsoft.com/downloads/details.aspx?familyid=6ed0fb7e-7c05-4f59-879a-8fb619e36612&displaylang=en

b.    Microsoft SQL Server 2000 Service Pack 4 Replication Components – The package installs the SQL Server 2000 SP4 replication components on the machine running the IIS server. The components are required for connecting the SQL Mobile database on a mobile device to a SQL Server 2000 SP4 database. Link to download: http://www.microsoft.com/downloads/details.aspx?familyid=a0241f9c-79b1-4011-81e3-2601aa701e02&displaylang=en

c.     Microsoft SQL Server 2000 Service Pack 3a Replication Components – The package installs the SQL Server 2000 SP3a SQL Server 2000 SP3a replication components on the machine running the IIS server. The components are required for connecting the SQL Mobile database on a mobile device to a SQL Server 2000 SP3a database.  Link to download: http://www.microsoft.com/downloads/details.aspx?familyid=a9e0cea0-6f8d-4625-af71-fc8d671f64b6&displaylang=en

d.     Microsoft SQL Server 2005 Mobile Edition Device SDK – The SDK contains the product installers files for installing SQL Server Mobile on the mobile devices, and it also contains the DLL’s and Header files required for developing SQL Mobile applications on the Desktop. Link to download: http://www.microsoft.com/downloads/details.aspx?FamilyId=5BD8ABAA-5813-4DB3-B23A-24551DE2ECC1&displaylang=en

e.     IBuySpy Delivery 2005 Developer Toolkit Sample Application– The Toolkit is a sample e-commerce storefront to demonstrate how simple it is to create scalable applications for the Microsoft® .NET® platform by using SQL Server Mobile . Link to download: http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=7A5F64B0-4E64-4A72-A2C9-994A0C55CECD .

Apart from these the following are also available – 

EULA for SQL Server Mobile Device SDK: http://download.microsoft.com/download/2/4/8/2482E95D-00FD-4374-BD15-57A8C4C34444/EULA.rtf

ReadMe file for SQL Server Mobile Device SDK: http://download.microsoft.com/download/2/4/8/2482E95D-00FD-4374-BD15-57A8C4C34444/SSMREADME.htm

Help File for the SQL Server Mobile: http://download.microsoft.com/download/2/4/8/2482E95D-00FD-4374-BD15-57A8C4C34444/ssm3chm.chm

 

12/9/2005 2:38:47 PM (GMT Standard Time, UTC+00:00)  #     | 
 Wednesday, September 21, 2005
9/21/2005 9:50:02 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, September 08, 2005

How many times you wished to be able to use data binding with the ListView control. Well.. there are a few implementations of the bindable ListView available on the web, including the one on the MSDN. But in my opinion the best implementation was done by Ian Griffins a few years ago. In fact I've re-used this code for the ListBoxEx that's a part of SDF. So, here's the ported to .NetCF version of the BindableListView control. It doesn't have the desing-time version, but you should be able to use it in the following way:

private BindableListView bindList;

 

public Form1()

{

      //

      // Required for Windows Form Designer support

      //

      InitializeComponent();

 

      bindList = new BindableListView();

      bindList.View = View.Details;

      bindList.Size = new Size(240, 100);

      bindList.Location = new Point(0, 0);

      this.Controls.Add(bindList);

 

      bindList.DataSource = CreateData();

}

 

private ArrayList CreateData()

{

      ArrayList list = new ArrayList();

 

      Person person = new Person();

      person.Id = 4123;

      person.Name = "Alex Yakhnin";

      list.Add(person);

 

      person = new Person();

      person.Id = 5327;

      person.Name = "Chris Tacke";

      list.Add(person);

 

      //…

      return list;

}

Where the Person class is:

public class Person

{

      private int id;

      private string name;

 

      public Person()

      {

 

      }

 

      public int Id

      {

            get

            {

                  return id; 

            }

            set

            {

                  id = value;

            }

      }

 

      public string Name

      {

            get

            {

                  return name;     

            }

            set

            {

                  name = value;

            }

      }

 

}

Download the BindableListView and a test client here:

BindableListViewClient.zip (11.62 KB)
9/8/2005 3:58:26 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Monday, August 29, 2005

I've made a few updates to the code I'd posted last time mainly by moving the text wrapping functionality inside of the ToolTip control, therefore making it more self contained and useful for other scenarious. I optimized the MeasureStringExtend function to use StringBuilder and improving its speed by 50-70%. Here's the new version:

private SizeF MeasureStringExtend(Graphics g, string text, Font font, int desWidth)

{

      int sp_pos = 0;

      int line = 0;

      int nWidth = 0;

      StringBuilder sb = new StringBuilder();

 

      //get original size

      SizeF size = g.MeasureString(text, font);

 

      if (size.Width > desWidth)

      {

            string[] words = text.Split(' ');

            for (int i = 0; i < words.Length; i++)

            {

                  sb.Append(words[i] + " ");

                  nWidth = (int)g.MeasureString(sb.ToString(), font).Width;

 

                  if (nWidth > desWidth)

                  {

                        //try to find a space

                        sp_pos = sb.ToString().LastIndexOf(" ");

                        if (sp_pos > 0)

                        {

                              //cut out the wrap line we've found

                              sb.Remove(0, sb.Length);

                              sb.Append(words[i] + " ");

                              line++;

                        }

                        else //no space

                        {

                              sb.Remove(0, sb.Length);

                              line++;

                        }

                  }

            }

            // Check for the last line

            if (sb.Length > 0)

            {

                  line++;

            }

      }

      else

      {

            line++;

      }

 

      return new SizeF(desWidth, (line * size.Height));

}

 

I've also created a VS 2005 and CF v2 version of the ListViewExtender and ListViewLabel classes. Instead of ApplicationEx and IMessageFilter I utilized my NativeWindow implementation. Here's the ListViewSubclass class, the sole purpose of which to catch the WM_LBUTTONDOWN message and raise the MouseDown event:

public class ListViewSubclass : NativeWindow

{

      public event MouseEventHandler MouseDown;

 

      private const int WM_LBUTTONDOWN = 0x0201;

      private ListView listView;

 

      public ListViewSubclass(ListView listView)

      {

            this.listView = listView;

            this.AssignHandle(listView.Handle);

      }

 

      protected override void WndProc(ref Message m)

      {

            if (m.Msg == WM_LBUTTONDOWN)

            {

                  int y = HiWord((int)m.LParam);

                  int x = LoWord((int)m.LParam);

                  MouseEventArgs args = new MouseEventArgs(0, 1, x, y, 0);

                  if (MouseDown != null)

                  {

                        MouseDown(listView, args);

                  }

            }

            base.WndProc(ref m);

      }

 

      private static int HiWord(int number)

      {

            if ((number & 0x80000000) == 0x80000000)

                  return (number >> 16);

            else

                  return (number >> 16) & 0xffff;

      }

 

      private static int LoWord(int number)

      {

            return number & 0xffff;

      }

}

Download the updated VS 2003 version from here.

ListViewToolTip2003.zip (10.08 KB)

And here's the VS 2005 version for your pleasure:

ListViewToolTip2005.zip (14.25 KB)

 

 

8/29/2005 4:40:50 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Friday, August 26, 2005

The ListView control in .NetCF has been one of the most useful for me and I think for many other developers. But it lacks a few important functions like the ability to show a long text in the wrapped mode or to edit items in the ListView. So I set myself on the road of trying to accomplish this and believe me, it was not a walk in the park, rather a rock climbing.
My first thought was that the easiest way to achieve the required functionality would be just to place either a Label or a TextBox on a top of the ListView. OK, so I need the location and the size of an item in the ListView. The Windows CE SDK docs state that it is possible to send the LVM_GETITEMRECT or LVM_GETSUBITEMRECT message to the ListView control and it will return the RECT structure with what we’re looking for. But these messages expect the indexes of item and subitem to be passed. Alright, then there is the LVM_SUBITEMHITTEST message that should provide us with the LVHITTESTINFO structure. This structure includes the item’s indexes as well as the POINT structure with the expected client coordinates. Now, it all comes down to getting the coordinates of the place where the ListView is being tapped. Unfortunatelly, ListView in .NetCF doesn’t implement mouse events, so we must resort to the extreme measures like subclassing the ListView control and catching mouse down message.  As you probably know, SDF provides the ApplicationEx and IMessageFilter classes and I had done my implementation of the pseudo window procedure as the WinProcFilter class long time ago. With all pieces of the puzzle in place I was ready finish the task.
First, I have created the ListViewExtender class that implements GetItemAt and GetItemRectangle methods:

public HitItem GetItemAt(int x, int y)

{

      HitItem hitItem = new HitItem();

 

      // Init LVHITTESTINFO structure

      LVHITTESTINFO hitTextInfo = new LVHITTESTINFO();

      hitTextInfo.x = x;

      hitTextInfo.y = y;

      hitTextInfo.flags = LVHT_ONITEM;

                 

      // Allocate native memory

      IntPtr pointer = AllocHGlobal(Marshal.SizeOf(hitTextInfo));

      // Copy LVHITTESTINFO structure to the memory pointer

      Marshal.StructureToPtr(hitTextInfo, pointer, false);

                 

      // Retreive item/subitem indexes

      SendMessage(handle, LVM_SUBITEMHITTEST, 0, pointer);

 

      hitTextInfo = (LVHITTESTINFO)Marshal.PtrToStructure(pointer, typeof(LVHITTESTINFO));

      FreeHGlobal(pointer);

 

      hitItem.ItemIndex = hitTextInfo.iItem;

      hitItem.SubItemIndex = hitTextInfo.iSubItem;

                 

      return hitItem;

 

}

 

public Rectangle GetItemRectangle(int itemIndex, int subItemIndex)

{

      RECT rect = new RECT();

      rect.left = 2;

      rect.top = subItemIndex;

      // Retreive item's rectangle

      SendMessage(handle, LVM_GETSUBITEMRECT, itemIndex, ref rect);

      // Convert to the screen coordinates

      Rectangle rc = listView.RectangleToScreen(new Rectangle(rect.left, rect.top - 26, rect.right - rect.left, rect.bottom - rect.top));

      return rc;

}

Next, I wanted to have a label control that could draw borders, so whipped up a custom control ToolTip that inherits from the Control and overrides its OnPaint event:

protected override void OnPaint(PaintEventArgs e)

{

      base.OnPaint (e);

      e.Graphics.Clear(this.BackColor);

      Rectangle textRect = this.ClientRectangle;

 

      textRect.X = 3;

      //textRect.Y = 2;

      textRect.Width += 2;

      textRect.Height += 2;

 

      e.Graphics.DrawString(this.Text, this.Font, textBrush, textRect);

 

      this.DrawBorder(e.Graphics);

}

After that, I’ve create the ListViewLabel class that is derived from ListViewExtender:

public class ListViewLabel : ListViewExtender, IDisposable

{

private ToolTip toolTip;

      private WinProcFilter winProcFilter;

      private const int WM_LBUTTONDOWN = 0x0201;

      private HitItem hitItem;

      private TextBox editText;

      private bool editable;

 

      public ListViewLabel(ListView listView, bool editable) : base(listView)

      {

            this.editable = editable;

            if (!editable)

            {

                  // Create instance of the Tooltip control

                  toolTip = new ToolTip();

                  toolTip.Visible = false;

                  // Add to the Form

                  listView.Parent.Controls.Add(toolTip);

            }

            else

            {

                  editText = new TextBox();

                  editText.Visible = false;

                  editText.LostFocus+=new EventHandler(editText_LostFocus);

                  // Add to the Form

                  listView.Parent.Controls.Add(editText);

            }

 

            // Init WinProcFiter

            winProcFilter = new WinProcFilter(this.handle);

            winProcFilter.WndProc+=new WinProc(winProcFilter_WndProc);

                 

      }

}

The constructor’s parameters are a ListView control and a Boolean value that will tell the control whether you want to edit the ListView item or just show the long content. We also create instance of the WinProcFilter class and hook up into its WndProc event:

private void winProcFilter_WndProc(ref Microsoft.WindowsCE.Forms.Message m)

{    

      if (m.Msg == WM_LBUTTONDOWN)

      {

            if (toolTip != null)

            {

                  toolTip.Hide();

            }

 

            if (editText != null)

            {

                  if (editText.Visible) // Update the item's text back

                  {

                        listView.Items[hitItem.ItemIndex].SubItems[hitItem.SubItemIndex].Text = editText.Text;

                        editText.Visible = false;

                  }

            }

 

            byte[] pos = BitConverter.GetBytes(m.LParam.ToInt32());

            int x = BitConverter.ToInt16(pos, 0); // LOWORD

            int y = BitConverter.ToInt16(pos, 2); // HIWORD

 

            Point clientPoint = listView.PointToClient(new Point(x, y));

 

            hitItem = this.GetItemAt(x, y);

            if (hitItem.ItemIndex > -1)

            {

                  // Get item's rectangle

                  Rectangle rc = GetItemRectangle(hitItem.ItemIndex, hitItem.SubItemIndex);

                       

                  // Get item's text, location and width

                  string itemText = listView.Items[hitItem.ItemIndex].SubItems[hitItem.SubItemIndex].Text;

 

                  SizeF size;

                 

                  if (!editable) // display label

                  {

                        toolTip.Text = itemText;

                        toolTip.Location = new Point(rc.Left, rc.Top);

                        toolTip.Width = rc.Width;

                        // Get the height of the wrapped text

                        using(Graphics gx = listView.Parent.CreateGraphics())

                        {

                              size = this.MeasureStringExtend(gx, toolTip.Text, toolTip.Font, toolTip.Width);

                        }

 

                        if (size.Height > rc.Height) // Display a Tooltip only on multilined items

                        {

                              toolTip.Height = (int)size.Height;

                              toolTip.BringToFront();

                              toolTip.Show();

                        }

                  }

                  else // display TextBox

                  {

                        editText.Text = itemText;

                        editText.Width = rc.Width;

                        editText.Location = new Point(rc.Left, rc.Top);

                        editText.BringToFront();

                        editText.Visible = true;

                  }

            }

      }

}

In the code above we check if we received the WM_LBUTTONDOWN message. If we did, we retrieve mouse coordinates from the LParam and get the HitItem by calling created earlier GetItemAt method. After that we can fetch the item’s rectangle which is used to place the ToolTip or TextBox controls on the top of the ListView. Here are the screenshots of the test client that’s using the ListViewLabel class:

As you can see, it was not an easy road indeed.

You can download the all of the code from here:

 

ListViewToolTip.zip (10.06 KB)
8/26/2005 6:55:51 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, August 18, 2005

The .NetCF doesn't provide implementation for the DropDownWidth property for the ComboBox control. Even though it is just a simple call to send either CB_SETDROPPEDWIDTH or CB_GETDROPPEDWIDTH messages. So, following the extender pattern that I aleady used to create the ListBoxExtender I've created the ComboBoxExtender class that implements the DropDownWidth property:

public class ComboBoxExtender

{

      private ComboBox comboBox;

      private IntPtr handle;

 

      public ComboBoxExtender(ComboBox comboBox)

      {

            this.comboBox = comboBox;

            // Get native handle

            this.comboBox.Capture = true;

            this.handle = GetCapture();

            this.comboBox.Capture = false;

      }

 

      public int DropDownWidth

      {

            get

            {

                  return GetDropDownWidth();

            }

            set

            {

                  SetDropDownWidth(value);

            }

      }

 

      private void SetDropDownWidth(int width)

      {

            SendMessage(handle, CB_SETDROPPEDWIDTH, width, 0);

      }

 

      private int GetDropDownWidth()

      {

            return SendMessage(handle, CB_GETDROPPEDWIDTH, 0, 0);

      }

 

      #region P/Invokes

 

      const int CB_GETDROPPEDWIDTH    =      0x015f;

      const int CB_SETDROPPEDWIDTH    =      0x0160;

      [DllImport("coredll.dll")]

      static extern IntPtr GetCapture();

  

      [DllImport("coredll.dll")]

      static extern int SendMessage(IntPtr hwnd, int msg, int wParam, int lParam);

 

      #endregion

}

You would use this class:

private ComboBoxExtender comboExt;

 

comboExt = new ComboBoxExtender(comboBox1);

 

comboExt.DropDownWidth = 250;

 

8/18/2005 6:58:40 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Wednesday, August 17, 2005

The best way to test functionality and usability of a library is to use it for development for some real project. So I took the OpenNETCF.Rss library for a spin and created a RSS reader for Pocket PC. I gave it a name “OpenRSS”. During its development I discovered a few bugs and missing functionality in the OpenNETCF.Rss, so the time spend was worth it. Here are a few screenshots:

The OpenRSS is a work in progress. It doesn’t implement all functionality that would be required for a production ready RSS reader, so consider it as a sample or a starting point.

You can download the OpenRSS’s project with updated OpenNETCF.Rss bits here:

OpenRSS.zip (180.63 KB)

I’ve also uploaded the OpenNETCF.Rss into the Vault, which means it should become a part of SDF in the future release.

8/17/2005 5:06:07 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, August 04, 2005

The “RSS”, “blogging”, “PODcasting”, “OPML” – these acronyms are becoming increasingly big in the technology world. Especially after Microsoft had announced about RSS support in the Longhorn err… Vista and IE7. The MSDN article mentions photo blogs, moblogs, and video blogs as well as calendar feeds. But wait a second. Why does it have to run on a desktop? I am pretty sure that all these usage scenarios are perfect fit Mobile devices as well. So here’s a hint for Mobile devices / developers teams at Microsoft – let’s extend the “RSS everywhere” paradigm to the Windows CE platform as well. Call me crazy, but I also think RSS feeds could become more then an XML over HTTP. RSS feeds and lists don’t have to be blogs. They could as well include any type of business information that needs to be updated on a regular basis. What prevents us to use RSS over different communication channels like UDP or TCP?

 

So having all these ideas in mind I have started on creation of Y.A.R.F (Yet Another RSS Framework). I know there’re a few .NET implementations available on the internet, but I didn’t like either of them – they’re not flexible or extensible enough. I wanted a framework that could be used on both .NetCF and full .NET, allowed usage of the configuration files and was able to process Atom feeds as well. So here it is – a technology preview of the OpenNETCF.Rss library. When designing the architecture for the framework I’ve tried to model it after a WSE which provides paradigms of “channels” or connections and “transports“ or protocols that’re used to transport the data. Here’s the class diagram of the OpenNETCF.Rss library:

When developing the OpenNETCF.Rss I’ve used some RSS and Atom parsing code from the most excellent Dare’s RSSBandit.

 

There're a few ways you can use this library to retreive a RSS feed.

 

1. Syncronously:

 

Feed feed = FeedEngine.Receive(new Uri(“http://myfeed/rss.aspx”));

 

2. Asyncronously through event handler:

 

FeedEngine.FeedReceived+=new FeedReceivedHandler(FeedEngine_FeedReceived);

FeedEngine.Subscribe(new Uri((“http://myfeed/rss.aspx”));

 

2. Asyncronously by providing an instance of your FeedReceiver:

 

public class MyFeedReceiver : FeedReceiver

{

      Form1 form;

 

      public AlexFeedReceiver(Form1 form)

      {

            this.form = form;

      }

     

//Implement the Receive

      public override void Receive(Feed feed)

      {

            form.ShowFeed(feed);

      }

}

 

...

 

FeedEngine.Subscribe(new Uri("http://www.engadget.com/rss.xml"), new MyFeedReceiver(this));

I'll elaborate on the OpenNETCF.Rss design in a later posts.

The OpenNETCF.Rss is in no way complete. I am planning on adding more functionality like FeedStorage and communications channels. Please do not hesitate to comment on the design and implementation. The VS.NET solution includes test projects for the desktop and PPC device.

OpenNETCF.Rss.zip (406.65 KB)

Documentation.chm (100.58 KB)
8/4/2005 3:36:22 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, July 14, 2005

I've seen the request on the CF newsgroups for the control that would be able to scroll and zoom in(out) an image. Immediatedly remembered that I had this kind of control developed for my AtlasCE for Smartphone. So I pulled down the code, cleaned it up and created a sample client:

The ImageViewer control's code doesn't have a lot of comments, but I hope that the code is self explanatory enough for you to figure out how it works.

The ImageView has just a handfull of public methods properties:

You can download the control's source code and a sample from here:

ImageViewerClient.zip (76.15 KB)
7/14/2005 10:11:05 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Tuesday, July 05, 2005

This question has just come up on the .NetCF newsgroup. P/Invoke is again to the rescue:

public void SaveTextBox(TextBox textBox, string fileName)

{

      StringBuilder buffer = new StringBuilder(" ", 255);

      // Get TextBox's native handle

      textBox.Capture = true;

      IntPtr handle = GetCapture();

      textBox.Capture = false;

     

      // The EM_GETLINE requires the length of the buffer in the first byte.

      buffer[0] = (char)255;

 

      using(StreamWriter writer = new StreamWriter(fileName))

      {

            // Get line count

            int lineCount = SendMessage(handle, EM_GETLINECOUNT, 0, 0);

 

            for(int i=0;i<lineCount;i++)

            {

                  SendMessage(handle, EM_GETLINE, i, buffer);

                  writer.WriteLine(buffer.ToString());

            }

            writer.Flush();

      }

 

}

 

Required P/Invoke declarations:

const int EM_GETLINECOUNT    =  0x00BA;

const int EM_GETLINE         =  0x00C4;

 

[DllImport("coredll.dll")]

static extern IntPtr GetCapture();

 

[DllImport("coredll.dll")]

static extern int SendMessage(IntPtr hwnd, int msg, int wParam, int lParam);

 

[DllImport("coredll.dll")]

static extern int SendMessage(IntPtr hwnd, int msg, int wParam, StringBuilder lParam);

 

7/5/2005 9:57:45 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, June 23, 2005

Continuing the theme of extending the functionality of the intrinsic controls in the .NetCF, I am going to show you the way to show the horizontal scrollbar in the ListBox.

In order to achieve this we once again turn to the Windows Mobile SDK docs. The docs state that we could use the LB_SETHORIZONTALEXTENT message to set the width of the scrollbar in the ListBox. But this will work only if the ListBox is defined with the WS_HSCROLL style which we should be able to modify by employing GetWindowLong and SetWindowLong API calls. I've taken the ListBoxExtender class from the previouse post and added the following code in it:

private void ModifyStyle(uint addStyle, uint removeStyle)

{    

      // Get current window style

      uint windowStyle = GetWindowLong(handle, GWL_STYLE);

 

      if (addStyle != 0)

      {

            // Modify style

            SetWindowLong(handle, GWL_STYLE, windowStyle | addStyle);

      }

      else

      {

            // Remove style

            SetWindowLong(handle, GWL_STYLE, windowStyle & ~removeStyle);

      }

      // Let the window know of the changes

      SetWindowPos(handle, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOZORDER | SWP_NOSIZE | SWP_FRAMECHANGED);

}

 

public void SetScrollWidth(int width)

{

      // Set the scroll width

      SendMessage(handle, LB_SETHORIZONTALEXTENT, width, 0);

}

 

public bool HorizontalScrollbar

{

      get

      {

            return horizontalScrollbar;

      }

      set

      {

            if (horizontalScrollbar == value)

                  return;

            horizontalScrollbar = value;

                       

            if (horizontalScrollbar)

            {

                  ModifyStyle((uint)WS_HSCROLL, 0);

            }

            else

            {

                  ModifyStyle(0, (uint)WS_HSCROLL);

            }

      }

}

We shouldn't forget the appropriate P/Invoke declarations:

const int LB_GETHORIZONTALEXTENT  = 0x0193;

const int LB_SETHORIZONTALEXTENT  = 0x0194;

 

const long WS_HSCROLL    =      0x00100000L;

 

const int SWP_FRAMECHANGED    = 0x0020;

const int SWP_NOMOVE          =     0x0002;

const int SWP_NOSIZE          =     0x0001;

const int SWP_NOZORDER        =     0x0004;

 

const int GWL_STYLE         =  (-16);

 

 

[DllImport("coredll.dll")]

static extern int SendMessage(IntPtr hwnd, int msg, int wParam, int lParam);

[DllImport("coredll.dll")]

static extern uint GetWindowLong(IntPtr hwnd, int index);

[DllImport("coredll.dll")]

static extern void SetWindowLong(IntPtr hwnd, int index, uint value);

[DllImport("coredll.dll")]

static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,

      int Y, int cx, int cy, uint uFlags);

You can download the full version of the ListBoxExtender here:

ListBoxExtender.zip (1.35 KB)

After the code's updated, you can use the ListBoxExtender class the following way:

listBoxEx = new ListBoxExtender(listBox1);

...

listBoxEx.HorizontalScrollbar = true;

listBoxEx.SetScrollWidth(200);

Now, the most attentive should be able to notice that I am passing some hardcoded value to the SetScrollWidth method, which of course is not going to work for all possible cases. In order to have an appropriate behaviour of the scrollbar you will have to indentify the longest string item in your ListBox and pass it to the SetScrollWidth .

6/23/2005 10:10:20 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Wednesday, June 22, 2005

As you know, the intrinsic controls (ListBox, TextBox, ComboBox etc..) in the .NET Compact Framework are just a wrappers around its native counterparts. It is also known that the managed versions do not expose all available functionality. For example, it is possible to search the items in the ListBox or ComboBox just simply sending a windows messages such as LB_FINDSTRINGEXACT or LB_FINDSTRING. In order to illustrate its usage I've created the ListBoxExtender class, that will expose Search methods in the easy to use form:

/// <summary>

      /// Summary description for ListBoxExtender.

      /// </summary>

      public class ListBoxExtender

      {

            private ListBox listBox;

            private IntPtr handle;

 

            public ListBoxExtender(ListBox listBox)

            {

                  this.listBox = listBox;

                  // Get native handle

                  this.listBox.Capture = true;

                  this.handle = GetCapture();

                  this.listBox.Capture = false;

            }

 

            /// <summary>

            /// Searches an item in the ListBox by the specified item value.

            /// </summary>

            /// <param name="item">Item to search.</param>

            /// <returns>The index of the found item.</returns>

            public int Search(string item)

            {

                  return Search(item, -1, true);

            }

 

            /// <summary>

            /// Searches an item in the ListBox by the specified item value.

            /// </summary>

            /// <param name="item">Item to search.</param>

            /// <param name="startIndex">The zero-based index of the item before the first item to be searched.</param>

            /// <returns>The index of the found item.</returns>

            public int Search(string item, int startIndex)

            {

                  return Search(item, startIndex, true);

            }

 

            /// <summary>

            /// Searches an item in the ListBox by the specified item value.

            /// </summary>

            /// <param name="item">Item to search.</param>

            /// <param name="startIndex">The zero-based index of the item before the first item to be searched.</param>

            /// <param name="exact">Specifies whether the exact or partial search is performed.</param>

            /// <returns>The index of the found item.</returns>

            public int Search(string item, int startIndex, bool exact)

            {

                  int result = -1;

 

                  if (exact)

                  {

                        result = SendMessage(handle, LB_FINDSTRINGEXACT, startIndex, item);

                  }

                  else

                  {

                        result = SendMessage(handle, LB_FINDSTRING, startIndex, item);

                  }

 

                  return result;

            }

 

            #region P/Invokes

 

            const int LB_FINDSTRING           = 0x018F;

            const int LB_FINDSTRINGEXACT      = 0x01A2;

 

            [DllImport("coredll.dll")]

            static extern IntPtr GetCapture();

            [DllImport("coredll.dll")]

            static extern int SendMessage(IntPtr hwnd, int msg, int wParam, string lParam);

 

            #endregion

      }

The usage of this class should be obviouse:

listBoxEx = new ListBoxExtender(listBox1);

...

listBox1.SelectedIndex = listBoxEx.Search("Some Item");

 

6/22/2005 5:48:35 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Wednesday, June 01, 2005

Managed controls in the Compact Framework don't expose all available functionality of the native controls they wrap. And that is understandable since the CF team wanted to make them fast and small. For example, ComboBox control doesn't implement the DropDown event that could be quite helpfull to store a previouse value because it should fire before the SelectedIndexChanged event. So I've decided to implement this event using the NativeWindow class I blogged about the other day.

The Windows CE SDK documentation states that there's a CBN_DROPDOWN notification message that is send to the parent of the ComboBox when the listbox of a combo box is about to become visible. This message is sent in the form of WM_COMMAND.

A quick trip to the winuser.h file to pull out the values for the CBN_DROPDOWN and WM_COMMAND and here's the ComboSubclass class that catches this message and raises the DropDown event:

public class ComboSubclass : NativeWindow

{

    private const int WM_COMMAND = 0x111;

    private const int CBN_DROPDOWN   =  7;

 

    public event EventHandler DropDown;

 

    private ComboBox comboBox;

 

    public ComboSubclass(ComboBox comboBox, Control parent)

    {

        this.comboBox = comboBox;

        //Subclass parent form

        this.AssignHandle(parent.Handle);

    }

 

    protected override void WndProc(ref Message m)

    {

        if (m.Msg == WM_COMMAND)

        {

            // Make sure that's its our ComboBox

            if (m.LParam == comboBox.Handle)

            {

                if (DropDown != null)

                {

                    // Raise the event

                    DropDown(comboBox, null);

                }

            }

        }

        base.WndProc(ref m);

    }

}

The usage of this class should be pretty straightforward:

   comboSubclass = new ComboSubclass(comboBox1, this);

  comboSubclass.DropDown += new EventHandler(comboSubclass_DropDown);

           

 

  void comboSubclass_DropDown(object sender, EventArgs e)

  {

      prevIndex = ((ComboBox)sender).SelectedIndex;

  }

Update: You can achieve the same goal in the CF v1 by utilizing ApplicationEx class from SDF and WinProcFilter I posted last year.

6/1/2005 2:14:42 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Monday, May 23, 2005

As you probably know, the CF team has added the capability to use managed delegates as a native callbacks in the CF v2. It means that it’s become quite possible to use API’s that require a callback function such as EnumFonts, EnumWindows etc… The same goes true for been able to use SetWindowLong to subclass window procedure. The full .NET Framework includes a very useful NativeWindow class that wraps window subclassing and creating new windows functionality in the very usable and convenient package. A few months ago, I’d created a .NET Compact Framework v2 implementation of the NativeWindow class with exactly the same functionality (AssignHandle, CreateHandle, WndProc etc...

 

So what can you do with this class? Subclass any control and catch the messages that are not exposed as events, create your own versions of the native controls (EDIT, LISTBOX, ListView etc…)

 

You should expect the NativeWindow to become a part of the Smart Device Framework for CF v2.

 

You can download the NativeWindow class and a test project from here:

NativeWindowTest.zip (62.02 KB)
5/23/2005 5:28:13 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, May 05, 2005

One of my favorite new features available in the CF v2 is the XmlSerializer. Presence of it in the CF opens up a great possibilities in building a cross device/desktop communications (read WSE and such). But when you start using it you'd notice some lag when trying to serialize/deserialize your objects. It's explained by a way the XmlSerializer is designed. When you call its constructor:

new XmlSerializer(type, defaultNamespace)

the serializer walks the whole object graph using reflection and discovers all serializable properties and fields. Reflection as you know, due to its “late bound“ nature, is not the fastest part of .NET. So the solution could be caching the XmlSerializer for the object types that already have been used and re-use it for next time you need the serializer.

Here's the code for XmlSerializerCache class that you can use to achieve that:

public class XmlSerializerCache

{

      #region fields

 

      private static Hashtable table;

 

      #endregion // fields

           

      #region constructors

 

      static XmlSerializerCache()

      {

            XmlSerializerCache.table = new Hashtable();

      }

 

      private XmlSerializerCache()

      {

      }

 

      #endregion // constructors

 

     

      public static XmlSerializer GetXmlSerializer(Type type, string defaultNamespace)

      {

            XmlSerializer serializer;

            if (type == null)

            {

                  throw new ArgumentNullException("type");

            }

            // Make it thread safe

            lock (XmlSerializerCache.table.SyncRoot)

            {

                  string typeName;

                  if ((defaultNamespace == null) || (defaultNamespace.Length == 0))

                  {

                        typeName = type.FullName + "#";

                  }

                  else

                  {

                        typeName = type.FullName + "#" + defaultNamespace;

                  }

                  // Try to get the serializer from cache

                  object obj = XmlSerializerCache.table[typeName];

     

                  if (obj == null)

                  {

                        // We don't have it, create a new instance

                        obj = new XmlSerializer(type, defaultNamespace);

                        XmlSerializerCache.table.Add(typeName, obj);

                  }

                  serializer = obj as XmlSerializer;

            }

            return serializer;

      }

}

 

The usage of the XmlSerializerCache should be pretty straightforward:

 

XmlSerializer serializer = XmlSerializerCache.GetXmlSerializer(typeof(Order), "http://tempuri.org/");

 

serializer.Serialize(stream, order);

 

BTW, you can employ this class for the desktop .NET as well too and enjoy some speed improvements.

5/5/2005 4:31:20 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Friday, February 04, 2005

In his recent post Mike Zintel divuldges some information on the past, present and future of evolution of the NetCF and asking some feedback on the priorities for v.Next. As I already pointed out in my previouse post, CF lacks essential support for developing SOA such as WS-* protocol suite or Indigo. So seeing that CF team is thinking on leveraging it is very encoraging sign. The only problem I see here is the timing. Mike mentions this in the context of V3, but we need it now. The project that I am working on is a real time trading system that utilizes desktop as well as CE devices clients. In the case of desktop the usage of WSE is no brainer and fits in the architecture that I designed pretty well. But in case of CE devices I have to go through some loops by utilizing a Web Service in the middle for outgoing messages and a custom UDP communication for incoming. Plus by the time the CF V3 will be coming out the Indigo should be available so it would make sence to implement Indigo for CF V3 as well. How about if CF team would release a WS-* for CF as and add-on not tied up to V3, but may be to some Service Pack for v2 of CF and provided a full support for Indigo in the V3. I think this should be a feasable option since MS already has the WSE in the v2.0 which is pretty stable and shouldn't pose a lot problems when porting the code to CF v2.

2/4/2005 3:32:23 PM (GMT Standard Time, UTC+00:00)  #     | 
 Wednesday, January 12, 2005

You should be aware that it's not feasible to call EnumWindows API in the CF, since the usage of the delegates as a native callbacks is not supported in the v1 (it will be supported in CF v2 though). But there's a way to emulate functionality of this API by using GetWindow and GetWindowText:

public static Window[] EnumerateTopWindows()

{

      ArrayList winList = new ArrayList();

      IntPtr hwnd = IntPtr.Zero;

      Window window = null;

      StringBuilder sb = null;

                 

      // Get the first window

      hwnd = GetActiveWindow();

      hwnd = GetWindow(hwnd, GW_HWNDFIRST);

     

      while(hwnd != IntPtr.Zero)

      {    

            IntPtr parentWin = GetParent(hwnd);

            // Make sure that the window doesn't have a parent

            if ((parentWin == IntPtr.Zero))

            {

                  int length       = GetWindowTextLength(hwnd);

                  // Check if it has caption text

                  if (length > 0)

                  {

                        window = new Window();

                        window.Handle = hwnd;

                        sb = new StringBuilder(length + 1);

                        GetWindowText(hwnd, sb, sb.Capacity);

                        window.Caption = sb.ToString();

                        winList.Add(window);

                  }

            }

                 

            hwnd = GetWindow(hwnd, GW_HWNDNEXT);

      }

      return (Window[])winList.ToArray(typeof(Window));

}

 Download the sample code from here.

EnumerateWindowsProj.zip (5.82 KB)
1/12/2005 5:03:23 PM (GMT Standard Time, UTC+00:00)  #     | 
 Wednesday, December 29, 2004

As you know CF v1 (it will be available in CF v2 though) doesn't provide implementaion of the FromHbitmap method that allows creation of the managed Bitmap object from native window handle (HBITMAP). The solution that I provide is based on the idea of creation of a managed Bitmap from an in memory array/stream of a bitmap file like for example is done in the XrossOne Mobile GDI+. We can get the information on the Bitmap using GetObject API including a pointer to the bitmap bits which we can copy to the managed array:

public static Bitmap FromHbitmap(IntPtr handle)

{

     BITMAP bm = new BITMAP();

     // Get info

     GetObject(handle, Marshal.SizeOf(bm), ref bm);

     // Create our Bitmap buffer

     BitmapBuffer bmpBuffer = new BitmapBuffer(bm.bmWidth, bm.bmHeight, (short)bm.bmBitsPixel);

     // Native pointer to bmp bits

     IntPtr pBits = new IntPtr(bm.bmBits);

     // Create temp array for bitmap bits to workaround a bug in the Marshal.Copy -

     // it never uses the startIndex

     byte[] tempBits = new byte[bm.bmWidthBytes * bm.bmHeight];

     //Get the bits to the temporary managed byte array.

     Marshal.Copy(pBits, tempBits, 0, bm.bmWidthBytes * bm.bmHeight);

     // Copy from the temporary managed byte to our buffer

     System.Buffer.BlockCopy(tempBits, 0, bmpBuffer.buffer, offset, tempBits.Length);

     // Create managed Bitmap

     return bmpBuffer.CreateBitmap();

}

You can download the whole implementation for the BitmapBuffer class and the sample here.

FromHbitmapProject.zip (6.63 KB)
12/29/2004 10:42:10 PM (GMT Standard Time, UTC+00:00)  #     | 
 Friday, December 10, 2004

 

There’s a known solution to enable HTTP 1.1 Compression for Web Services that’s been described on dotnetjunkies site. The solution that they propose is to override GetWebRequest and GetWebResponse methods in your WS generated proxy file. If you’d try to implement this solution for CF you’d get an “no suitable method found to override" exception. The problem is that Microsoft “forgot” to expose these methods as overridable in the WebClientProtocol class in the v1 of CF, but… they have silently updated the WebClientProtocol class to expose the GetWebRequest and GetWebResponse as virtual (overridable) methods in one of the Service Packs.

So, in order to be able to compile the sample from dotnetjunlies, you'd need to update the System.Web.dll in the \CompactFrameworkSDK\v1.0.5000\Windows CE directory. Here's the tricky part. The steps are:

 

1. Download the CF SP2 developer redistributable:

 

http://www.microsoft.com/downloads/details.aspx?familyid=10600643-09b3-46d8-ba28-bc494bc20d26&displaylang=en

 

2. Unpack one of the CAB's from the distribution (I've used WinRAR to unpack the netcf.core.ppc3.x86.CAB file).

 

3. Rename the SYC6B2~1.014 file to System.Web.dll

 

4. Copy it to the fore mentioned directory.

 

Here you go, now you should be able to modify the web service proxy files to use using Standard HTTP 1.1 Compression.

 

Oh… and also make sure you’re running the SP2 bits on the device as well.

 

I’ve put together a CF sample that includes SharpZipLib and the code from the article. The sample uses the StockService sample Web Service from WSE 2.0 distribution.

 

You can download the sample here.

WSCompression.zip (226.13 KB)
12/10/2004 4:11:00 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, December 07, 2004

A question has recently popped up on CF newsgroups on how to connect to a network resource programmatically. There's no managed API's exist to handle that, but as always native API's and P/Invoke comes to our rescue. On a desktop as well as in Windows CE this functionality's been handled by a network API's such as WNetAddConnection3, WNetCancelConnection2 etc... The documentation on them is pretty straightforward.

[DllImport("coredll")]

private static extern int WNetAddConnection3(

                  IntPtr hwndOwner,

                  NETRESOURCE lpNetResource,

                  string lpPassword,

                  string lpUserName,

                  int dwFlags);

 

[DllImport("coredll")]

private static extern int WNetCancelConnection2(

                  string lpName,

                  int dwFlags,

                  int fForce);

The WNetAddConnection3 accepts as a parameter the pointer to the NETRESOURCE structure so I declared it as the following:

private class NETRESOURCE

{

      public int dwScope;

      public int dwType;

      public int dwDisplayType;

      public int dwUsage;

      public IntPtr lpLocalName;

      public IntPtr lpRemoteName;

      public IntPtr lpComment;

      public IntPtr lpProvider;

}

In order to assign the values to lpLocalName and lpRemoteName members we'll use the MarshalEx.StringToHGlobalUni call from SDF. So, the call to WNetAddConnection3 looks like that:

NETRESOURCE NetRes = new NETRESOURCE();              

      NetRes.dwScope = RESOURCE_GLOBALNET | RESOURCE_REMEMBERED;

      NetRes.dwType = RESOURCETYPE_DISK;

      NetRes.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;

      NetRes.dwUsage = RESOURCEUSAGE_CONNECTABLE;

      NetRes.lpRemoteName = MarshalEx.StringToHGlobalUni(netRes);

      NetRes.lpLocalName = MarshalEx.StringToHGlobalUni(shareName);

      NetRes.lpComment = IntPtr.Zero;

      NetRes.lpProvider = IntPtr.Zero;

                       

      int ret = WNetAddConnection3(hwnd, NetRes, password, userName, 1);

 

      if (ret != 0)

      {

            throw new System.ComponentModel.Win32Exception(ret, ((NetworkErrors)ret).ToString());

      }                      

 After you've made successfull mapping you should be able to access your network resource by using System.IO's functionality like File.Copy, File.Create, File.Delete etc... Keep in mind that since Windows CE does not support drive letters, your share name will be mapped to the reserved root \Network. So if you named your share as “REMOTE“, you'll access it as “\Network\REMOTE\somefile.txt”.

You can download the source code for the Network class and a sample here.

MapDriveProject.zip (7.34 KB)
12/7/2004 3:25:23 PM (GMT Standard Time, UTC+00:00)  #     | 
 Monday, December 06, 2004

I've updated the SIPControl to draw images in place of buttons. It required just minimal changes to the code. I added Image and ImageDown properties to the KeyButton class, and added an new override to the DrawButton method to support image drawing:

private void DrawButton(string text, Graphics gx, Rectangle rc, Color foreColor, Image image)

{

      Brush foreBrush = new SolidBrush(foreColor);

     

      // draw image

      gx.DrawImage(image, rc.Left, rc.Top);

      //Center the text

      SizeF size = gx.MeasureString(text, this.Font);

           

      int X = rc.Left + (int)(rc.Width - size.Width)/2;

      int Y = rc.Top + (int)(rc.Height - size.Height)/2;

                 

      gx.DrawString(text, this.Font, foreBrush, X, Y);

                 

      foreBrush.Dispose();

}

I've also modified the OnPaint method to recognize if the KeyButton has an image assigned. So now, if you'd want to have image to draw a button, the client code would look like that:

ButtonRow row1 = new ButtonRow(new string[]{"q","w","e","r","t","y","u","i","o","p","[","]"}, 5, 20);

row1.Space = 0;

//Assign images

foreach(KeyButton button in row1.Buttons)

{

      button.Image = bitmap;

      button.ImageDown = bitmapDown;

}

And here's the screen shot of the sample:

Download the new version of the SIPControl and a sample here.

SIPTest1.zip (10.05 KB)

12/6/2004 5:51:37 PM (GMT Standard Time, UTC+00:00)  #     | 
 Friday, December 03, 2004

As you probably know that if you would want to implement a full “kiosk” mode on Pocket PC – which means that you need to hide the “Start” and SIP buttons to prevent users getting into underlying system. It’s easy to employ – just remove a MainMenu from a Form, set ControlBox to false and set the WindowState = FormWindowState.Maximized in the form’s Load event. But what if I still need clients to use SIP to enter some text? We could use the SipShowIM API, but this call will make the SIP button to appear again breaking the “kiosk” mode. So, to solve this puzzle I’ve created the SIPControl  - an owner-drawn control that allows creation of any type of SIP’s. Here’s the code snip that shows how to create a full SIP keyboard on your form:

sipFull = new SIPControl();

                 

// Location and size

sipFull.Location = new Point(1, 60);

sipFull.Size =    new Size(240, 90);

     

//Create ButtonRows

ButtonRow row1 = new ButtonRow(new string[]{"Q","W","E","R","T","Y","U","I","O","P"}, 1, 0);

row1.Space = 4;

// Add ButtonRow to the Rows collection

sipFull.Rows.Add(row1);

ButtonRow row2 = new ButtonRow( new string[]{"A","S","D","F","G","H","J","K","L"}, 10, 22);

row2.Space = 4;

sipFull.Rows.Add(row2);

ButtonRow row3 = new ButtonRow( new string[]{"Z","X","C","V","B","N","M"}, 30, 44);

row3.Space = 4;

sipFull.Rows.Add(row3);

ButtonRow row4 = new ButtonRow(  new string[]{"1","2","3","4","5","6","7","8","9","0"}, 1, 66);

row4.Space = 4;

sipFull.Rows.Add(row4);

                 

// Hook up the Click event

sipFull.ButtonClick+=new ButtonClickEventHandler(sipFull_ButtonClick);

this.Controls.Add(sipFull);        

 And here’s how the sample looks:

 

You can download the SIPControl and a sample from here.

SIPTest.zip (7.8 KB)
12/3/2004 4:44:53 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, November 23, 2004

In his recent post Alex Feinman shows how to create XmlDataSource. This is a really great and usefull piece of code that shows how to implement databinding by the means of xml file.

If your your XML data is not that complex and close to the DataSet's xml serialized schema you can use the Paul Wilson's Open-Source ADO.NET XML Provider, that implements appropriate .NET Framework Data Provider interfaces and allows accessing and filtering the XML data by way of sql statements. I've ported the code for Compact Framework. Here's the screen shot of a sample client:

You can download the source code from here.

XmlDbProvider.zip (147.42 KB)

 

11/23/2004 7:48:30 PM (GMT Standard Time, UTC+00:00)  #     | 
 Wednesday, November 17, 2004

Chris Forsberg of pocketpcdn reminds us about this really cool resource created by Microsoft: We Rock 247. The Smart Client Sample download includes all required SDK's, tools and a source code for web client, winform client, web services and mobile client. The mobile part includes a few applications for Pocket PC and Smartphone (Packman and RoadRunner) and shows how to implement MVC pattern, MD5 password hashing, exception tracing, database wrapper (a la stored procedures), forms stack and many more really useful stuff.

11/17/2004 7:25:36 PM (GMT Standard Time, UTC+00:00)  #     | 
 Monday, October 18, 2004

Everybody has been talking about Service-Oriented Architecture (SOA) lately. And it’s not about Web Services, but essentially about service been abstracted from the implementation, published specs of its interface and a formal contract between service provider and its customer.  So, mostly it is about standards in the communication between different platforms and systems – this is where SOAP, WSE (Indigo in the future) come to play. It’s all nice and dandy when you develop SOA systems for desktops/servers, but we’d immediately step into uncharted territory if you try to apply SOA principals when developing for mobile devices. Yes, we can call to Web Services from .NET CF client, we can even communicate with servers which implement WSE (big thanks to Casey for his wonderful work here), but you’d fall short if your project should require a 2 way real-time communication with a desktops or other devices in the almost always connected (WIFI) environment. So this rant is to bring attention of Microsoft folks on this very important subject. Mobile devices are not only used in the end-user retail environment, but also in the enterprise. We need WSE or Indigo for mobile devices.

10/18/2004 3:03:05 AM (GMT Daylight Time, UTC+01:00)  #     | 
 Tuesday, September 14, 2004

In this old post of mine I was asking for more access to the native windows and graphics handles in CF. Things are really getting better in the Compact Framework V2. We get what we were asking for.  The Graphics.FromHdc is there, as well as Bitmap.LockBits (UnlockBits) and Image.Save. Although the Bitmap.GetHbitmap and Image.FormHandle are still seems to be missing.

We still don't have the ability to access WinProc of the managed control or to host a native control in the managed environment, but hopefully this will be changed when (if) CF V2 will support native callbacks.

9/14/2004 2:34:51 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Wednesday, September 08, 2004

A lot of questions on inter process communication (IPC) in Compact Framework have recently popped up on the CF NG. And one of the options for IPC is to use Memory Mapped Files (MMF). So what are those? Here's the quote from MSDN documentation:

“A memory-mapped file, or file mapping, is the result of associating a file's contents with a portion of the virtual address space of a process. It can be used to share a file or memory between two or more processes...”

Windows CE supports both named and unnamed file-mapping objects. I'd say that named object to be more useful.  If the created object has a name you can easily pass that name to other processes so these processes can access the same object.

While doing investigation on my current project possibility of IPC, I've made some research on available C# code that wraps MMF. I've found quite a few implementations available on various web sites (CodeProject.com, Gotdotnet.com, etc...), but I've decided to port the implementation of MemoryMappedFileStream that comes with the Smart Client Offline Application Block. The conversion to CF was easy - changed the import dll to coredll.dll and added some functionality to handle named MMF's.

You can download the project from here.

9/8/2004 6:52:00 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, August 26, 2004

While doing modeling for the new project that requires a HTTPS communication by posting some xml messages, I've encountered a few intricacies in the Compact Framework. First problem I’ve hit when trying to use the code that’s been working fine for me in the previous project to send a POST request over SSL is the known bug that throws a Socket exception. To workaround it I had to stop using ContentLength of the request class and set the AlllowWriteStreamBuffering to true. Another problem I ran into is that I was getting a TrustFailure exceptions due to a security certificate on the web server I was accessing is not fully trusted. A quick search on the Google brought up a great post by Jan Tielens that suggested implementing the ICertificatePolicy interface. It worked like a charm. The last problem came into play when I discovered that the web site requires passing a session cookies after logging in. That was easily resolved by getting the cookies from the WebResponse.Headers collection and setting it back in the request object. Here’s the snippet of the final working code:

request = (HttpWebRequest)WebRequest.Create(url);

request.Method = "POST";

request.ContentType="application/x-www-form-urlencoded";

//request.ContentLength = parameters.Length;

request.AllowWriteStreamBuffering  = true; //required for bug workaround

request.Timeout = 60000;

     

if (sessionCookies!="")

{    

      string[] cookies = sessionCookies.Split(';');

      // We've got some cookies

      if (cookies.Length > 0)

      {

            request.Headers.Add("Cookie", cookies[0]);

      }

      else

      {

            request.Headers.Add("Cookie", sessionCookies);

      }

}

//Get request stream

Stream requestStream = request.GetRequestStream();

//Write a post values

requestStream.Write(Encoding.ASCII.GetBytes(parameters), 0, parameters.Length);

requestStream.Close();

// Get response

response = (HttpWebResponse)request.GetResponse();

if (sessionCookies == "")

{

      //Get the cookies

      sessionCookies = response.Headers["Set-Cookie"];

}

 

reader = new StreamReader(response.GetResponseStream());

result = reader.ReadToEnd();

response.Close();

 

8/26/2004 2:25:12 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, July 15, 2004

I've been busy these days with a new major CF project at some famous company, but I don't want you to miss a few important and interesting things happening on the CF horizon:

1. I am sure you're seen the announcement about OpenNETCF Coding Competition.

2. Mark Clifton of the MyXaml fame has created a forum for using MyXaml with the Compact Framework. You can already download the bits for CF. Very interesing stuff!

3. How about a Class Browser running on the device?

7/15/2004 4:02:28 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Monday, June 28, 2004

Seth's posted an invaluable info on Managed App Startup and Retargeting. I wish we had more of these “brain dumps“.

 

6/28/2004 4:30:39 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, June 24, 2004

I've just posted a new article on OpenNETCF.org: Creating a Custom Marshaler for the .NET Compact Framework

Enjoy...

 

6/24/2004 8:40:15 PM (GMT Daylight Time, UTC+01:00)  #     | 

David Kline is blogging about possibility to use cordbg to debug .NetCF. Really great stuff! He's just posted the part II with explanation of modes and commands when debugging. And more info will be coming in! My only gripe is why this information could not be available earlier.

6/24/2004 1:26:53 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Wednesday, June 02, 2004
The recent competition at Codeproject.com produced a slew of motivating applications for Compact Framework. Some of them show quite interesting solutions (many of them by the way directly reference or reuse the code from SDF) in resolving the challenges of the device development.  My favorites are:

Apply Animated Effects to .NET Controls

Thin Client for Microsoft Visual SourceSafe on Pocket PC / SmartPhones

Pocket PC based Animation Editor

Wireless Pocket PC media Remote for PC

ForestPad - a method for storing and retrieving textual information

6/2/2004 1:51:31 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Wednesday, May 12, 2004

I've been following a heated discussions on XML parsing methods that exist in .NET right now: DOM and XmlReader, whereas the first one is the slowest and the second is fast but a more complicated to code against. A must read on this topic is the DonXML's post Waking Up From a DOM Induced Coma who underlines the existing problems with XML parsing and mentions a few other alternatives. All of the problems about XML parsing are relevant to .NetCF as well, therefore they are multiplied due to processing power and memory constraints of the WinCE devices. Don't forget a complete lack of XPath implementation in the current version of CF.

As lucky as we are, Dare Obasanjo and Co., a PM from Microsoft, have just posted another library: XPathReader. And as you can guess, I've immediately pulled the code from the article and tried to compile it against .NetCF... The result of the test was that it runs with a few minor changes to the original code!!! And it is more exiting since we get a some XPath functionlity as well. Here you can see a snapshot of the test program running in the PPC emulator:

Don't you love when something useful comes with the source?

You can download the test and a XPathReaderCF projects from here.

The next step will be to test a comparison performance of this library vs XmlDocument in CF.

5/12/2004 5:30:34 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Tuesday, May 11, 2004

One of the FAQ on the CF newsgroup has been a question on how to send email from WinCE device with attachments or without it. We know that on Pocket PC's emailing functionality is a part of POOM API's as well as exposed in some form of MAPI implementation. All of these options are not easy to implement from .NetCF.  Plus POOM is not available on non PPC devices... But how about just using a SMTP protocol over TCP/IP connection? A quick search on the internet returned quite a few .NET implementations. Due to its simplicity I decided to go with Peter Bromberg's excellent article as a starting point. The code in the article references missing from .NetCF MailMessage and MailAttachment classes which shouldn't be a big problem to implement... Here is the screenshot of the testing program:

A quick test showed that I can successfully send attachments as well.

Download the code here.

5/11/2004 3:22:31 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Tuesday, March 23, 2004

Just to prove in the hundred's time that there's nothing impossible exist for him, Alex Feinman has posted this article on OpenNETCF.org. It shows how to create a managed Icon object from native icon handle. Brilliant job (as always) Alex! 

3/23/2004 8:14:19 PM (GMT Standard Time, UTC+00:00)  #     | 

Brad is describing some new stuff coming with Whidbey for GC. Considering a much more memory constrained environment of CE devices, GC.AddMemoryPressure/GC.RemoveMemoryPressure and HandleCollector would be really nice to have for Compact Framework too!

3/23/2004 3:55:36 PM (GMT Standard Time, UTC+00:00)  #     | 
 Friday, March 19, 2004

I've finally released AtlasCE for SmartPhone 2003 which I think is one of the first commercial .NET CF programs for Smartphones. Aside from learning the intricacies of the smartphone UI, I had to create a key-press searching/filtering mechanism similar to the built-in Contacts application, a scrolling image viewer control with zooming functionality and other more interesting stuff. It was always like that for me - creating some program would lead to creation of a few “by-products” that could be utilized later on in some other applications. I'll try to show you how it's all been done in a later blog entries. Meanwhile I can tell you that I've used a lot of code from SDF: ListBoxEx, Configuraton and HTMLViewer.  

3/19/2004 8:28:03 PM (GMT Standard Time, UTC+00:00)  #     | 
 Thursday, March 18, 2004

Alright, so I've got this new and shiny blog now! Big thanks to Neil for spending his valuable time converting me to the dasBlogs. It has so much stuff that I will have to spend some time looking around.

Meanwhile, back to the subject.

In Windows programming there is a notion of Virtual ListBoxes or ListViews. It means that a list box would allow you to have unlimited amount of the displayed items. It is usually done by telling it how many items there should be and it simply delegates all drawing of items to the owner of the list box. Under Windows CE the list box can’t be made virtual, only a ListView. But it should be really easy to implement by utilizing OwnerDrawnList base control, which I described here and which has become a part of SDF.

So, here are the steps to implement the VirtualListBox:

 

  1. Derive from OwnerDrawnList to create a new class:

public class VirtualListBox : OwnerDrawnList

{

// Private members

      int count;

      private ArrayList itemList;

      // Constructor

      public VirtualListBox()

      {

            itemList = new ArrayList();

            this.ShowScrollbar= true;

      }

 

      // Property to the number of items

      public int Count

      {

            get

            {

                  return count;

            }

            set

            {

                  if (count!=value)

                  {

                        count = value;

                        itemList.Clear();

                        int[] items = new int[count];

                        itemList.AddRange(items);

                  }

            }

      }

 

      // if you don’t want to draw the item yourself just use this method

      public void DefaultDrawItem(DrawItemEventArgs e, String text)

      {

            Brush textBrush;

 

            Rectangle rcText = e.Bounds;

 

            //Add 3 pixels

            rcText.X += 3;

 

            if (e.State == DrawItemState.Selected) // selected                      {

                  e.DrawBackground();

                  textBrush = new SolidBrush(SystemColors.HighlightText);

            }

            else

            {

                  e.DrawBackground(this.BackColor);

                  textBrush = new SolidBrush(SystemColors.WindowText);

            }

 

// Draw the string

            e.Graphics.DrawString(text, this.Font, textBrush, rcText);

                 

            // Dispose the brush

            textBrush.Dispose();

 

      }

 

      // This is the required by base class override

      protected override System.Collections.IList BaseItems

      {

          get

          {

             return itemList;

          }

      }

           

       // This one required by base class too, but it will do nothing for us.

       protected override object BuildItemForRow(object row)

       {

             return null;

       }

 

   }

 

 

  1. Now we’re ready to use this control on the form. Add these lines to your form’s constructor:

 

InitializeComponent();

 

virtList = new VirtualListBox();

            virtList.Bounds = new Rectangle(1, 1, 200, 100);

            virtList.BackColor = Color.White;

            virtList.Count = 100;

            virtList.ItemHeight = 14;

           

            // Add the control to the form’s controls collection

            this.Controls.Add(virtList);

            // Let’s hook up into DrawItem event

         virtList.DrawItem+=new DrawItemEventHandler(virtList_DrawItem);

 

  1. Implement drawing event in your form:

private void virtList_DrawItem(object sender, DrawItemEventArgs e)

      {

        // You can draw the items yourself here or just delegate to the list itself.   

        virtList.DefaultDrawItem(e, "Virtual Item " + e.Index.ToString());

   }

 

 

3/18/2004 8:26:33 PM (GMT Standard Time, UTC+00:00)  #     | 
 Wednesday, March 17, 2004
Peter  has posted a new article on OpenNETCF.org about hosting Native windows controls. In order to achieve that he had to use some low level hacks by changing window style of the MessageWindow and I absolutely agree with Peter - we need WndProc and native handles in CF in the v2. And don't forget Graphics.FormHdc, Bitmap.GetHBitmap and Icon.FromHandle! Having a powerful framework is great, but it can't wrap ALL available native functionality, so Microsoft, please, give us ability to develop what's missing ourselves.
3/17/2004 4:45:37 PM (GMT Standard Time, UTC+00:00)  #     | 
 Friday, January 30, 2004

One of my wild google searches today have returned this jem: Compact Framework Architecture. This is a presentation by Frank Perchel-Galee which reviews a CF architecture. The most interesting slides for me are the ones that show the memory pools in the CLR. There 3 memory pools allocated: Metadata Cache, JIT Cashe and GC Pools. Memory reclaiming by GC is happenning in 3 phases:
Phase1: Mark and sweep
Phase2: Compact whatever is left
Phase3: Flush JIT Cache

That could mean that if your program requires quite a lot of memory to run, the once JIT'ed code could be flushed and would force JITer to kick in again for the code that's already been JIT'ed. One more interesting bit from one of the slides says that the compile time of JIT compiler is LINEAR to length of the method. This leads to immediate conclusions that if you want to get a good performance from your CF app, try to brake your long winded methods to a few shorter ones. It could save you some time during execution...

 

Oh and don' forget this MSDN article Writing High Performance Managed Applications. Most of the stuff mentioned there is applicable to CF too.

1/30/2004 2:02:55 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, January 27, 2004

As you know, the program memory on Pocket PC and Smartphone devices supposed to be handled by the OS itself. Meaning that shell would start sending WM_CLOSE messages if there is less than 128 KB of free memory. In some cases when running you application before allocation of some big chunk of memory you'd need to make sure that there's enough memory available for that operation. There is the SHCloseApps API function in the aygshell library that will do just what you need. We could also utilize the GlobalMemoryStatus to verify if such a drastic measures are required:

public bool FreeProgramMemory(int memSought)

{                

      bool result = true;

      MEMORYSTATUS memStatus = new MEMORYSTATUS();

      GlobalMemoryStatus(ref memStatus);

      //check if we're out of memory first

      if( memStatus.dwAvailPhys < memSought)

      {

            if (SHCloseApps(memSought)!= 0)

                  result = false;

      }

      return result;

}

//Required P/Invoke declarations

[DllImport("aygshell.dll")]

public static extern int SHCloseApps(int  dwMemSought);

 

[DllImport("coredll.dll")]

public static extern  void GlobalMemoryStatus(

            ref MEMORYSTATUS lpBuffer );

 

public struct MEMORYSTATUS

{

      public int dwLength;

      public int dwMemoryLoad;

      public int dwTotalPhys;

      public int dwAvailPhys;

      public int dwTotalPageFile;

      public int dwAvailPageFile;

      public int dwTotalVirtual;

      public int dwAvailVirtual;

}

 

1/27/2004 2:20:36 PM (GMT Standard Time, UTC+00:00)  #     | 
 Thursday, January 22, 2004

I don't know how about you, but for me visiting The Code Projects at least once a day have become a pleasant and necessary habit. It's really a valuable web site with a wealth of useful samples and projects for .NET. So, the other day I've stumbled upon this article on the web site. It explains how to create thumbnail images from a directory of Adobe Acrobat PDF documents.  The author gives details on a technique of using a template image for a thumbnail, making it transparent and overlaying it on a top of resized original image.  The code in the article is using available in .NET GDI+ methods like Image.GetThumbnailImage, Bitmap.MakeTransparent, etc… So I asked myself a question: “Is it still possible to implement a similar technique in Compact Framework?” Aside from saving the resulting image to a file I couldn’t see any problems in doing that and in 15 minutes I had this code…

 

The simplified implementation of the GetThumbnailImage goes first:

 

public static Bitmap GetThumbnailImage(Bitmap image, int height, int width)

{

    Bitmap bmp = new Bitmap(height, width);

    //create temp Graphics

    Graphics gx = Graphics.FromImage(bmp);

    //Resize the image

    gx.DrawImage(image, new Rectangle(0, 0, height, width), new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);

    //don't forget the dispose

    gx.Dispose();

    return bmp;

}

 

Now the CF version of the code from the article:

 

int thumbnailWidth = 38;

int thumbnailHeight = 52;

 

//Load original bitmap first

Bitmap orig = new Bitmap(@"\Program Files\ThumbnailProj\today.PNG");

//Create a thumbnailed version

Bitmap thumbOrig = GetThumbnailImage(orig, thumbnailWidth, thumbnailHeight);

//Load a template image

Bitmap thumbTemp = new Bitmap(@"\Program iles\ThumbnailProj\template_portrait.gif");

//Create new blank bitmap

Bitmap thumbnailBitmap = new Bitmap(thumbnailWidth + 7, thumbnailHeight + 7);

                 

Graphics gx = Graphics.FromImage(thumbnailBitmap);

gx.Clear(Color.White);

//Draw a thumbnailed image version  first

gx.DrawImage(thumbOrig, 2, 2);

           

//Draw a template image on the top with transparent attributes.

//This is what we do instead of Bitmap.MakeTransparent()

ImageAttributes attr = new ImageAttributes();

attr.SetColorKey(thumbTemp.GetPixel(5, 5), thumbTemp.GetPixel(5, 5));

gx.DrawImage(thumbTemp, new Rectangle(0, 0, thumbTemp.Width, thumbTemp.Height), 0, 0, thumbTemp.Width, thumbTemp.Height, GraphicsUnit.Pixel, attr);

 

//Draw the final image on the Form/Control

Graphics gxScr = this.CreateGraphics();

gxScr.DrawImage(thumbnailBitmap, 10, 10);



You can download the whole demo project here.
1/22/2004 2:31:49 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, January 20, 2004

You probably already know about a brilliant ApplicationEx class created by Chris for OpenNETCF.  It allows getting every single Windows message that your app receives.  I 've put together a IMessageFilter implementation that would filter out the messages that are send to a particular control on your Form or to the Form itself if you wish... Here is the code that for IMessageFilter implementation:

//Delegate to raise a event to a client

public delegate void WinProc(ref Message m);

 

class WinProcFilter : IMessageFilter

{

      public WinProc WndProc;

      private IntPtr handle;

 

      public WinProcFilter(Control ctl)

      {

            //Get control's window handle           

            ctl.Capture = true;

            this.handle = GetCapture();

            ctl.Capture = false;

      }

      #region IMessageFilter Members

      public bool PreFilterMessage(ref Message m)

      {

            bool handled = false;

            if (WndProc!=null)

            {

                  if (m.HWnd == handle) //Check window handle event

                  {

                      if (WndProc!=null) 

                       WndProc(ref m); //Raise the event

                  }

            }

            return handled;

      }

      #endregion

 

      [DllImport("coredll.dll")]

      private static extern IntPtr GetCapture();

}

On the client form you would need to declare and instanciate WinProcFilter class:

//Let's get the winproc for some TextBox

WinProcFilter procFilter = new WinProcFilter(textBox1);

//Hook up to WinProc event

procFilter.WndProc+=new WinProc(textBox1_WinProc);

.....

 

private void textBox1_WinProc(ref Message m)

{

    //Do whatever you like with the messages.

}

 

1/20/2004 5:02:04 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, January 13, 2004

You've probably noticed that the "Home" key press is not possible to catch in either of the Key event handles of the Form or Control. The solution to that will be to use the RegisterHotKey API in conjunction with MessageWindow:

using System;

using Microsoft.WindowsCE.Forms;

using System.Windows.Forms;

using System.Runtime.InteropServices;

public class MessageWin : MessageWindow

{

    //event for client

    public System.EventHandler HomeKeyPress;

 

    public MessageWin()

    {

       //register to listen for home key

       RegisterHotKey(this.Hwnd, VK_LWIN, 8, VK_LWIN);

    }

  

    ~MessageWin()

    {

       Unregister();

    }

 

    protected override void WndProc(ref Message m)

    {

       if (m.Msg == WM_HOTKEY)

       {

          if ((int)m.WParam == VK_LWIN)

             //Raise event

             if (HomeKeyPress!=null)

                   HomeKeyPress(this, null);

       }

       base.WndProc (ref m);

    }

 

  #region P/Invokes

    private const int VK_LWIN    =    0x5B;

    private const int WM_HOTKEY   =      0x0312;

    [DllImport("coredll.dll")]

    public static extern bool RegisterHotKey(

         IntPtr hWnd, // handle to window

         int id, // hot key identifier

         int Modifiers, // key-modifier options

         int key //virtual-key code);

    [DllImport("coredll.dll")]

    public static extern bool UnregisterHotKey(

         IntPtr      hWnd,

         int         id);

   #endregion

 }

The usage of this class should be pretty obviouse:

MessageWin  msgWin = new MessageWin();

msgWin.HomeKeyPress+=new EventHandler(msgWin_HomeKeyPress);

 

private void msgWin_HomeKeyPress(object sender, System.EventArgs e)

{

      MessageBox.Show("Home Key Pressed.");

}

 

1/13/2004 9:27:59 PM (GMT Standard Time, UTC+00:00)  #     | 
 Monday, January 12, 2004

To draw a rectangle with rounded corners is not a trivial task in Compact Framework due to lack of ability to draw arcs (Graphics.DrawArc, GraphicPaths in the big .NET, etc..). OK, so why don't we just use Graphics.DrawEllipse to draw a rounded corners instead? Definitelly we can do that, but we would have to somehow wipe out the internal arcs:

So, my solutuion to that problem is to create a poligon shape that would fill the rectangle with the required back color and would wipe out unneeded arcs:

public static void DrawRoundedRectangle(Graphics g, Pen p, Color backColor, Rectangle rc, Size size)
{
   Point[] points = new Point[8];
   
   //prepare points for poligon
   points[0].X = rc.Left + size.Width / 2;
   points[0].Y = rc.Top + 1;
   points[1].X = rc.Right - size.Width / 2;
   points[1].Y = rc.Top + 1;

   points[2].X =  rc.Right;
   points[2].Y = rc.Top + size.Height / 2;
   points[3].X =  rc.Right;
   points[3].Y = rc.Bottom - size.Height / 2;

   points[4].X =  rc.Right - size.Width / 2;
   points[4].Y = rc.Bottom;
   points[5].X =  rc.Left  + size.Width / 2;
   points[5].Y = rc.Bottom;

   points[6].X =  rc.Left + 1;
   points[6].Y = rc.Bottom - size.Height / 2;
   points[7].X =  rc.Left + 1;
   points[7].Y = rc.Top + size.Height / 2;

   //prepare brush for background
   Brush fillBrush = new SolidBrush(backColor);

   //draw side lines and circles in the corners
   g.DrawLine(p, rc.Left  + size.Width / 2, rc.Top,
    rc.Right - size.Width / 2, rc.Top);
   g.FillEllipse(fillBrush, rc.Right - size.Width, rc.Top,
    size.Width, size.Height);
   g.DrawEllipse(p, rc.Right - size.Width, rc.Top,
    size.Width, size.Height);

   g.DrawLine(p, rc.Right, rc.Top + size.Height / 2,
    rc.Right, rc.Bottom - size.Height / 2);
   g.FillEllipse(fillBrush, rc.Right - size.Width, rc.Bottom - size.Height,
    size.Width, size.Height);
   g.DrawEllipse(p, rc.Right - size.Width, rc.Bottom - size.Height,
    size.Width, size.Height);
   
   g.DrawLine(p, rc.Right - size.Width / 2, rc.Bottom,
    rc.Left  + size.Width / 2, rc.Bottom);
   g.FillEllipse(fillBrush, rc.Left, rc.Bottom - size.Height,
    size.Width, size.Height);
   g.DrawEllipse(p, rc.Left, rc.Bottom - size.Height,
    size.Width, size.Height);
   
   g.DrawLine(p, rc.Left, rc.Bottom - size.Height / 2,
    rc.Left, rc.Top + size.Height / 2);
   g.FillEllipse(fillBrush, rc.Left, rc.Top,
    size.Width, size.Height);
   g.DrawEllipse(p, rc.Left, rc.Top,
    size.Width, size.Height);
   
   //fill the background and remove the internal arcs  
      g.FillPolygon(fillBrush, points);
   //dispose the brush
   fillBrush.Dispose();
  }

And this is how you'd use this method:

Rectangle rc =  new Rectangle(10, 10, 100, 30);

DrawRoundedRectangle(e.Graphics,

new Pen(Color.Black), Color.CadetBlue, rc, new Size(8, 8));

 
1/12/2004 11:52:30 PM (GMT Standard Time, UTC+00:00)  #     | 
 Wednesday, December 24, 2003

The current project I am working on required me to implement ”AutoSize" functionality. It means that I needed to draw a relatively long text into a width limited rectangle. In that case a text will be wrapped and I need to calculate a height of this wrapped text. In the .NET Framework one of the overrides of Graphics.MeasureString would do the job, but in CF this override is not implemented. Of course there is a GetTextExtentExPoint API that could be used to do the same, but it expects as a parameter handle to the device context which is not freely available in Compact Framework. So I decided to create a managed version of this function.

<DISCLAIMER>The algorithm that's used in this function is not optimized for speed and size. If you should come up with improvents to that let me know.</DISCLAMER>

  public SizeF MeasureStringExtend(Graphics g, string text, Font font, int desWidth)
  {
   string tempString = text;
   string workString = "";
   int npos = 1;
   int sp_pos = 0;
   int line = 0;
   int nWidth = 0;

   //get original size
   SizeF size = g.MeasureString(text, font);

   if (size.Width > desWidth)
   {
    while(tempString.Length > 0)
    {
     //Check for the last lane
     if (npos > tempString.Length)
     {
      line++;
      break;
     }
     workString = tempString.Substring(0, npos);
     //get the current width
     nWidth = (int)g.MeasureString(workString, font).Width;
     //check if we've got out of the destWidth
     if (nWidth > desWidth)
     {
      //try to find a space
      sp_pos = workString.LastIndexOf(" ");
      if (sp_pos > 0)
      {
       //cut out the wrap lane we've found
       tempString = tempString.Substring((sp_pos + 1), tempString.Length - (sp_pos + 1));
       line++;
       npos = 0;
      }
      else //no space
      {
       tempString = tempString.Substring(npos, tempString.Length - npos);
       line++;
       npos = 0;
      }
     }
     npos++;
    }
   }
   return new SizeF(desWidth, (line*size.Height)) ;
  }


 

12/24/2003 6:40:05 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, December 23, 2003

XAML, WinFX, Indigo are the hottest buzz words in the IT these days. And those are very cool technologies indeed. Neil has expressed his dream of getting some of it for Smart Devices and I strongly second his motion.

Of course it's a technology that's still been developed.  But how feasible would be to implement XAML functionality for Compact Framework right now?  The most usability of XAML in this scenario I'd see its dynamic or "run-time" behavior. A lot of stuff could be done just using a simple reflection and XmlTextReader. I've put together a "proof of concept" project that could read an xml file and build a Form with controls on the fly.  Probably, it could become a starting point for one of the OpenNETCF.org projects. What're your thoughts on possible usage of XAML for Smart Devices?

12/23/2003 2:27:18 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, December 09, 2003

You've probably noticed that some of the applications on Pocket PC 2003 and SmartPhones have this fancy looking gradient background. In all these cases it looks like a system list view control have been used... A quick search through a PPC 2003 SDK returns this result in the  List View Styles: "LVS_EX_GRADIENT Draws a background with a gradient. " Bingo! We're almost there. We know that ListView control in Compact Framework is just a wrapper for a native one, so here is the code that'll do the job:

//Some PInvokes and constants
[DllImport("coredll.dll")]
public static extern IntPtr GetCapture();

[DllImport("coredll.dll")]
private static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);

const int LVS_EX_GRADIENT  =       0x20000000;
const int LVM_SETEXTENDEDLISTVIEWSTYLE  = 0x1000 + 54;

private void SetGradient(ListView listView)
{
     //Get a list view handle
     listView.Capture = true;
     IntPtr hList = GetCapture();
     //Apply extended style
     SendMessage(hList, (uint)LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_GRADIENT);
     listView.Capture = false;
     listView.Refresh();
}

12/9/2003 2:12:11 PM (GMT Standard Time, UTC+00:00)  #     | 
 Friday, December 05, 2003

VS.NET provides really sophisticated debugging options for developing SDP for Compact Framework. But sometimes there are situations when the only way to see what's happening in the code is to output some text/values to the Output window (or Console), but this option, as you know is missing from VS IDE. Although there's a solution to that - to implement System.Diagnostic.TraceListener and then to register it with the listeners collection. Take a look at this wonderful article on CodeProject.com. The sample from the article had successfully worked for me from devices and emulator with no problems.

12/5/2003 10:10:42 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, December 02, 2003

There was a question on the CF newsgroups the other day on how to implement functionality of the Control.GetNextControl in the Compact Framework. Knowing that a tab order in the CF should correspond to the order of the controls added into the Form.Controls collection, I came up with the following snippet:

public Control GetNextControl(Control ctl,  bool forward)
{
   int currIndex = this.Controls.IndexOf(ctl);

   if (forward)
   {
       //check if it's the last control in the collection
       if (currIndex < this.Controls.Count)
               currIndex++;
      else
               currIndex = 0; //move to the first one
   }
   else //backwards
   {
       //check if it's the first control in the collection
       if (currIndex > 0)
             currIndex--;
      else
             currIndex = this.Controls.Count - 1; //move to the last one
   }
   return this.Controls[currIndex];
}

 

 

12/2/2003 3:34:44 PM (GMT Standard Time, UTC+00:00)  #     | 

As Neil has just announced (SDF) Smart Device Framework is available for beta testing. It included all of the stuff we've developed before plus a whole bunch of really cool things we've been working on in the background. The OpenNETCF.Windows.Forms workspace includes the Owner-Drawn List I've described in the article  which has evolved into a fully fledged ListBoxEx control with full design-time support, background bitmap and data binding, the colorfull CheckBox control, StatusBar and much more really cool controls. As Neil's pointed in the new classes there is a must have XmlSerializer and FileSystemWatcher etc... On a top of that Neil has done a tremendous work to create a installation that integrates into Visual Studio .NET. And of course everything will come with a complete source code...

12/2/2003 1:54:18 AM (GMT Standard Time, UTC+00:00)  #     | 
 Wednesday, November 26, 2003

There is no secret that SQL CE currently is not supported on a Smartphone platform. It means that the options for data storage on a Smartphone for .NET Compact Framework developers are generally limited to DataSet XML serialization/deserialization. This option has known deficiencies: you can read/write data in “all or nothing” way only and XML by itself bloats the data, all of which is a big downsize in the memory constrained Smartphone environment. So, what’s the solution? Write your own “a read from the file stream, save in binary form, fast forward-only reading” storage in CF. And this is what I’ve been working on for the last few weeks. The quick test I ran yesterday showed a really good speed/memory benchmarks. DataGrid population with 1000 (5 fields) records takes about 3 secs! Now, I just need to find some time to clean up the code and write up a whitepaper on it…

11/26/2003 1:52:14 PM (GMT Standard Time, UTC+00:00)  #     |