Alex Yakhnin, .NET Compact Framework MVP

Subscriptions

< February 2007 >
Su Mo Tu We Th Fr Sa
28 29 30 31 1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 1 2 3
4 5 6 7 8 9 10

Navigation

WPF adventures (Part II)
In the previous entry I mentioned that the project’s UI design required me to create an interface similar to the Office 2007 UI. I decided to start with seems to be a simple part: tab control. In the proposed design it looks like that:

While the standard Tab looks like this:

So I started to play with the available properties of the Tab control in XAML. While I managed to apply the appropriate gradient backgrounds, I still needed to modify the shape of the tabs and the background of selected tabs. The internet search yielded the suggestion to use the Interactive Designer to create a custom template and style for the standard WPF’s TabControl. And I desided to fiddle with the Interactive Designer, thinking that if this tool is created for a non programmer type in mind, I should be able to figure it out pretty easy. I took the Martin’s guide for creation of a glass button and tried to follow the steps to create a required style for the Tab. That was a mistaken assumption. I immediately got lost in the hierarchy of the objects. After many tries, I finally came upon the SimpleStyles sample which is a part of the .NET 3.0 SDK. It is located in the Controls\ControlTemplateExamples\XAML folder and contains minimal templates for all standard controls. So I took the template for the Tab control as a base and using the VS started modifying XAML by hand. As it occurred later, this was the best decision for me to get things done. Every time I needed to get the custom style for a standard control, the SimpleStyles sample proved to be the best way to approach creation of the custom templates. You can download the resulting XAML from here:

posted August 30, 2006 11:15 AM by ayakhnin with 1 Comments

WPF adventures (Part I)
Recently I was submerged into the WPF. I had to create a proof of concept screens for one of the clients. The UI that was designed closely resembled the Office 2007 interface with Ribbon and such. So I bravely jumped into WPF. Installed .NET 3.0, Orcas extensions for VS, Interactive Designer and started on the UI. It is only after I jumped, I realized that am in a completely new world of the client development, that the learning curve has become a “learning cliff” and all of the tricks that I had learned when developing WinForm applications don't work here anymore. I had to start learning basics. Layouts and positioning, coloring, fonts and text etc... And as an early adapter you encounter with limited information available on the subject. There's only one book exists at this time (Chris Sells and Ian Griffits). Everything else is spread around in the WPF blogs, MSDN forums and documentation. When starting development with WPF you are presented with just a somewhat XP like looking standard controls. How do you change the look and feel of the control? Umm... inherit from the control and override some painting procedure... Right? - Nope, wrong! You create your own template and style in XAML! After 4 days of intensive “how do I do that, try, learn, search, download sample, try again...”, I started to see the light. The bits started to fall into pieces and the world had become a logical place to live again. With a mix of amusement and excitement I realized that MS had finally achieved its long winding goal - separate the presentation code from the functionality. A somewhat sophisticated UI that I have created had almost non existent C# code. Everything else has been done in XAML. In the next posts I'll try to share a few tips that I learned during this process and hope they'll help somebody ease up the transition.

posted August 30, 2006 10:52 AM by ayakhnin with 0 Comments

A ligthweight XmlDataReader.

In order to minimize the size of the CAB size for the game, include the WM Smartphone 2003 as well as reduce the steps required to install the Mobile Kombat on the user’s devices, one of the main requirements was to use CF v1. We also decided not to use the SQL CE 2.0 for the same purposes. One of the data files used in the game was a set of questions and answers for the trivia on mobile programming.  The obvious solution was to use DataSet for the data files, but don’t forget that it loads the whole Xml data file into memory and we were trying really hard to reduce a memory footprint of the game as much as possible. Let’s say we have 2 tables defined: Answer and Question. Here’s how the xml data looked like:

 

<NewDataSet>
  <Question>
    <QuestionID>1</QuestionID>
    <Text>Which is a new .NET Compact Framework 2.0 namespace?</Text>
    <CorrectAnswer>3</CorrectAnswer>
  </Question>
  <Question>
    <QuestionID>2</QuestionID>
    <Text>What operating system runs on Ultra Mobile PCs?Text>
    <CorrectAnswer>0</CorrectAnswer>
  </Question>
  …
<Answer>
    <QuestionID>1</QuestionID>
    <Text>SocketsText>
  </Answer>
  <Answer>
    <QuestionID>1</QuestionID>
    <Text>Collections</Text>
  </Answer>
  <Answer>
    <QuestionID>1</QuestionID>
    <Text>Cryptography</Text>
  </Answer>

 

So I came up with the solution of creating a forward only lightweight XmlDataReader that would move from one xml node to another and will create an instance of the appropriate class based on the xml data. Take a look at the class diagram:

  

Well… you could say that XmlSerializer can just do that. But remember that we had to use the CF v1, which doesn’t provide the XmlSerializer. Considering the time constraints that would be required to create a generic XmlSerializer, my solution was that all classes would have to implement the IXmlDataFormat interface:

 

interface IXmlDataFormat
{
    void ReadXml(XmlNodeList nodeList);
}

 

So the XmlDataReader when deserializing the objects will delegate the population of their properties to the object itself. Here’s how the object’s instance creation looks:

  

 

///
/// Creates an instance of the type if it implements the IXmlDataFormat interface.
///
/// The XmlNode.
///
private object GetValue(XmlNode node)
{           
     ConstructorInfo info = null;
     // Make sure that the type implements IXmlDataFormat interface.
     if (ImplementsInterface(type, typeof(IXmlDataFormat)))
     {
        info = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[0], null);
        if (info == null)
        {
             throw new ArgumentException("No constructor");
        }
        // Create an instance
        IXmlDataFormat objValue = info.Invoke(null) as IXmlDataFormat;
        if (objValue != null)
        {
            // Call the interface method
            objValue.ReadXml(node.ChildNodes);
        }
        return objValue;
     }
     return null;
  }  

 

And here’s how an actual class would implement the IXmlDataFormat interface:

 

public void ReadXml(XmlNodeList nodeList)
{
    foreach (XmlNode node in nodeList)
    {
        XmlElement element = node as XmlElement;
        if (element != null)
        {
            switch (element.LocalName)
            {
                case "QuestionID":
                {
                    this.questionID = Convert.ToInt32(element.InnerText);
                    continue;
                }
                case "Text":
                {
                    this.text = element.InnerText;
                    continue;
                }
                case "CorrectAnswer":
                {
                    this.correctAnswer = Convert.ToInt32(element.InnerText);
                    continue;
                 }
            }
         }
      }
   }
}

 

The code above just simply iterates through xml nodes and populates the object’s properties with the values from xml data.

 

I’ve also created the XmlDataManager class. This class implements a few useful helper methods that wrap calls to the XmlDataReader. Here’s some sample code that will retrieve all answers from xml data file by a certain question id:

 

// Create an instance of the XmlDataReader
XmlDataReader answersReader = new XmlDataReader(this.triviaFilePath, typeof(Answer));
// Create an instance of the XmlDataManager by passing the XmlDataReader
XmlDataManager answersManager = new XmlDataManager(answersReader);
// Retreive the list of anwers
IList answersList = answersManager.GetObjectList("QuestionID", QID);


 

You can download the sample project and the source code for the XmlDataReader here:

 

 

XmlDataReaderTest.zip (48.11 KB)

posted May 16, 2006 3:04 PM by ayakhnin with 0 Comments

I am back..
I am back from MEDC in Vegas. As I anticipated it, the conference was an unending mini twister of different events, sessions, meeting old friends, fellow MVP’s, OpenNETCF team, Microsoft’s guys from CF and other teams and new people, sumobot competition, Mobile Kombat game we’ve developed for MEDC that Nick described in his blog. In the next few posts I’ll describe a few interesting solutions I had to come up with to make the game to have a minimal memory footprint and appropriate performance.

posted May 15, 2006 10:44 AM by ayakhnin with 0 Comments

Some background on persistent storage.

I would like to refer you to these great reads that explain the intricacies of the persistent storage in the Windows CE 5.0 devices:

http://blogs.msdn.com/windowsmobile/archive/2006/03/16/552996.aspx

http://blogs.msdn.com/ce_base/archive/2006/03/15/IncreaseFSThroughput.aspx

The first one is from Mike Caligaro from Windows Mobile Team and the second is form Ariane Jensen who is a member of the Windows CE core team. A must read for both.

posted March 17, 2006 11:41 AM by ayakhnin with 0 Comments

Origami, origami...

So, everybody have been talking about Origami lately. It's started from the first Microsoft's unobscure marketing pitch on this site and finaly culminated after offical CeBIT announcements. I would agree with many people that the UMPC devices are a hardly revolutional idea, but it has got a great potential to become a new trend in the way people would use the computer, making it more usable for the average Joe.

So what's about the developer story for these devices? Since it runs Windows XP, it is a full .NET, baby! But consider a screen size, resolution and relatively less powerful processor - what does it remind you? Right, it is a pumped up version of the Windows Mobile device. And the people who have been developing for Windows CE based devices should feel themselves right at home. Need to create a smaller screen, touch driven user interface application? No problem here. 800x480 screen size is a football field for us and we know how to utilize it without cramping up the whole screen, draining the battery and making functional and easy to use. So WM and Windows CE developers suddenly have become experts in developing for UMPC! By the way, the beta of the UMPC emulator is already available for download here. It enables you to test your application's layout and screen behavior as it appears on UMPC's. But remember it is not a hardware emulator. It just puts the UMPC skin on your screen and resizes your desktop work area to the resolution on the UMPC (800x480).

posted March 9, 2006 11:11 AM by ayakhnin with 0 Comments

Keyboard hook in the CF v2

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.


        

posted March 5, 2006 4:54 PM by ayakhnin with 0 Comments

How to display images in the ListView header.

Ever wanted to display images in the header of a ListView control in the .NET CF application? Sure we can do that. The Windows CE SDK documentation reveals that practically all messages that are available on a desktop are supported by the ListView. In particular we should be interested in HDM_SETITEM message which supposed to be send to the ListView header with the pointer to the HDITEM structure.  Fifteen minutes later I had this class ready:

public class ListViewHeaderIcon

{

        IntPtr hWndHeader;

        ListView listView;

 

        public ListViewHeaderIcon(ListView listView)   

        {

            this.listView = listView;

            //Get HWND of the header

            hWndHeader = SendMessage(listView.Handle, LVM_GETHEADER,

             IntPtr.Zero, IntPtr.Zero);

        }

        public void SetHeaderImage(int columnIndex, int imageIndex, bool placeOnRight)

        {

            //Make sure that we have handle of the header

            if (hWndHeader == IntPtr.Zero)

                throw new Exception("Handle of header does not exist.");

 

            //Create HDITEM and populate with values

            HDITEM hdItem = new HDITEM();

 

            hdItem.mask = HDI_TEXT | HDI_IMAGE | HDI_FORMAT;

            hdItem.fmt = HDF_STRING | HDF_IMAGE | (placeOnRight ? HDF_BITMAP_ON_RIGHT : 0);

            hdItem.iImage = imageIndex;           

            hdItem.pszText = Marshal.StringToBSTR(listView.Columns[columnIndex].Text);

                     

            //Sending HDM_SETITEM message

            SendMessage(hWndHeader, HDM_SETITEM, (IntPtr)columnIndex, ref hdItem);

 

        }

 

        #region P/Invokes

 

         const uint HDM_FIRST = 0x1200;

         const uint HDM_SETITEM = HDM_FIRST + 12;

         const uint HDI_FORMAT = 0x0004;

         const uint HDI_TEXT = 0x0002;

         const uint HDI_BITMAP = 0x0010;

         const uint HDI_IMAGE = 0x0020;

         const int HDF_STRING = 0x4000;

         const int HDF_BITMAP = 0x2000;

         const int HDF_IMAGE = 0x0800;

         const int HDF_BITMAP_ON_RIGHT = 0x1000;

         const uint LVM_FIRST = 0x1000;

         const uint LVM_GETHEADER = LVM_FIRST + 31;       

 

        [DllImport("coredll.dll")]

         static extern IntPtr SendMessage(IntPtr hWnd,

         uint uMsg, IntPtr wParam, ref HDITEM lParam);

 

        [DllImport("coredll.dll")]

        static extern IntPtr SendMessage(IntPtr hWnd,

        uint uMsg, IntPtr wParam, IntPtr lParam);

       

        struct HDITEM

        {

            public uint mask;

            public int cxy;

            public IntPtr pszText;

            public IntPtr hbm;

            public int cchTextMax;

            public int fmt;

            public int lParam;

            public int iImage;

            public int iOrder;

            public uint type;

            public IntPtr pvFilter;

        }

 

        #endregion

 

 }

As you can see, the SetHeaderImage accepts the index of the column, index of the image from the ImageList that is ssigned to the SmallImageList property of the ListView and a boolean that you can use to show the image left or right justified.

In order to use this class, drop ther ListView on your form, populate it with some data. Don't forget to add the ImageList with some images and assign it to the SmallImageList property:

ListViewHeaderIcon header = new ListViewHeaderIcon(listView1);

 

for (int i = 0; i < listView1.Columns.Count; i++)

{

     header.SetHeaderImage(i, i, false);

}

 

posted February 24, 2006 5:46 PM by ayakhnin with 0 Comments

Redirect to Alex Yakhnin's Blog

Alex's Blog is hosted at OpenNETCF.org. You can access it here.

This blog will soon be updated to consume the RSS feed from Alex's Blog. Stay tuned.

posted February 17, 2006 10:16 AM by ActiveNick

Powered by Community Server, by Telligent Systems