Friday, August 12, 2005

My current assignment with www.nymex.com is close to completion, so I am available for projects/job in Mobility/.NET Compact Framework/.NET Architect in NY/NJ (and not only) area. You can contact me at a.yakhnin@(REMOVETHIS)att.net

Little bit about myself here.

 

8/12/2005 8:45:07 PM (GMT Daylight Time, UTC+01:00)  #    Comments [26]  | 
 Tuesday, August 09, 2005

I've updated the OpenNETCF.Rss library I posted about last time. The IFeedStorage interface has been added. It exposes the following methods:

public interface IFeedStorage

{          

          ///

          /// Inits the storage provider.

          ///

          /// Represents a single node in the XML document

          void Init(XmlNode section);

 

          ///

          ///   Adds an element with the specified key and value into the storage.

          ///

          void Add (Feed feed);

           

          ///

          ///   Removes all elements from the storage.

          ///

          void Flush ();

           

          ///

          ///   Gets the element with the specified key.

          ///

          Feed GetFeed (string key);

           

          ///

          ///   Removes the element with the specified key.

          ///

          void Remove (string key);

           

          ///

          ///   Updates the element with the specified key.

          ///

          void Update (Feed feed);

           

          ///

          ///   Gets the number of elements actually contained in the storage.

          ///

          int Size{ get; }

}

 

I've also created the FeedFileStorage class, that inherits from IFeedStorage and implements the functionality to store RSS feeds in the file system. You can provide your own implementaion of the IFeedStorage that could store feeds in the database or in some other location. The FeedEngine class now has the Storage property:

public static IFeedStorage Storage

It will return the currently enabled feed storage which is dynamically loaded from the config file settings:

<storage>

      <provider name="fileStorage" enabled="true" type="OpenNETCF.Rss.FeedFileStorage,OpenNETCF.Rss,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null">

            <path value="C:\Temp\Channels" />

            <feedSizeLimit value="100" />

      </provider>

storage>

So in order to load your own feed storage you'd need to add a similar section to the config file and modify the enabled flag appropriately. The path and feedSizeLimit settings are specific to the file storage. You can provide your own parameters and load them in the IFeedStorage.Init method.

Here's the updated version of the library as well as test clients:

OpenNETCF.Rss1.zip (425.74 KB)
8/9/2005 8:00:06 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)  #     | 
 Wednesday, July 20, 2005

Be sure to visit all the options under "Configuration" in the Admin Menu Bar above. There are 16 themes to choose from, and you can also create your own.

 

7/20/2005 8:00:00 AM (GMT Daylight Time, UTC+01:00)  #    Comments [4]  | 
 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)  #     |