Writing a Managed Internet Explorer Extension: Part 2

Continuing my miniseries from Writing a Managed Internet Explorer Extension: Part 1, we discussed how to setup a simple Internet Explorer Browser Helper Object in C# and got a basic, but somewhat useless, example working. We want to interact with our Document Object Model a bit more, including listening for events, like when a button was clicked. I’ll assume that you are all caught up on the basics with my previous post, and we will continue to use the sample solution.

Elements in the HTMLDocument can be accessed by getElementById, getElementsByName, or getElementsByTagName, etc. We’ll use getElementsByTagName, and then filter that based on their “type” attribute of “button” or “submit”.

objexplorerAn issue that regularly comes up with using the generated .NET MSHTML library is its endless web of delegates, events, and interfaces. Looking at the object explorer, you can see that there are several delegates per type. This makes it tricky to say “I want to handle the ‘onclick’ event for all elements.” You couldn’t do that because there is no common interface they all implement with a single onclick element. However, if you are brave you can let dynamic types in .NET Framework 4.0 solve that for you. Otherwise you will have a complex web of casting ahead of you.

Another issue that you may run into is conflicting member names. Yes, you would think this isn’t possible, but the CLR allows it, I just don’t believe C# and VB.NET Compiles allow it. For example, on the interface HTMLInputElement, there is a property called “onclick” and an event called “onclick”. This interface will not compile under C# 4:

   1: public interface HelloWorld

   2: {

   3:     event Action HelloWorld;

   4:     string HelloWorld { get; } 

   5: }

However, an interesting fact about the CLR is it allows methods and properties to be overloaded by the return type. Crazy, huh? Here’ is some bare bones MSIL you can compile on your own using ilasm to see it in action:

   1: .assembly extern mscorlib

   2: {

   3:   .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )

   4:   .ver 4:0:0:0

   5: }

   6:  

   7: .module MislExample.dll

   8: .imagebase 0x00400000

   9: .file alignment 0x00000200

  10: .stackreserve 0x00100000

  11: .subsystem 0x0003

  12: .corflags 0x0000000b

  13:  

  14: .class interface public abstract auto ansi MislExample.HelloWorld

  15: {

  16:   .method public hidebysig newslot specialname abstract virtual 

  17:           instance void  add_HelloWorld

  18:             (class [mscorlib]System.Action 'value') cil managed

  19:   {

  20:   }

  21:  

  22:   .method public hidebysig newslot specialname abstract virtual 

  23:           instance void  remove_HelloWorld

  24:             (class [mscorlib]System.Action 'value') cil managed

  25:   {

  26:   }

  27:  

  28:   .method public hidebysig newslot specialname abstract virtual 

  29:           instance string  get_HelloWorld() cil managed

  30:   {

  31:   }

  32:  

  33:   .event [mscorlib]System.Action HelloWorld

  34:   {

  35:     .addon instance void MislExample.HelloWorld::

  36:             add_HelloWorld(class [mscorlib]System.Action)

  37:     .removeon instance void MislExample.HelloWorld::

  38:             remove_HelloWorld(class [mscorlib]System.Action)

  39:   }

  40:   .property instance string HelloWorld()

  41:   {

  42:     .get instance string MislExample.HelloWorld::get_HelloWorld()

  43:   }

  44: }

That MSIL isn’t fully complete as it lacks any sort of manifest, but it will compile and .NET Reflector will be able to see it. You might have trouble referencing it from a C# or VB.NET project.

You can work around this issue by being explicit in this case: cast it to the interface to gain access to the event or do something clever with LINQ:

   1: void _webBrowser2Events_DocumentComplete(object pDisp, ref object URL)

   2: {

   3:     HTMLDocument document = _webBrowser2.Document;

   4:     var inputElements = from element in document.getElementsByTagName("input").Cast<HTMLInputElement>()

   5:                     select new { Class = element, Interface = (HTMLInputTextElementEvents2_Event)element };

   6:     foreach (var inputElement in inputElements)

   7:     {

   8:         inputElement.Interface.onclick += inputElement_Click;

   9:     }

  10: }

  11:  

  12: static bool inputElement_Click(IHTMLEventObj htmlEventObj)

  13: {

  14:     htmlEventObj.cancelBubble = true;

  15:     MessageBox.Show("You clicked an input element!");

  16:     return false;

  17: }

This is pretty straight forward: whenever the document is complete, loop through all of the input elements and attach on onclick handler to it. Despite the name of the interface, this will work with all HTMLInputElement objects.

Great! We have events wired up. Unfortunately, we’re not done. This appears to work at first try. However, go ahead and load the add on and use IE for a while. It’s going to start consuming more and more memory. We have written a beast with an unquenchable thirst for memory! We can see that in Son of Strike, too.

MT Count TotalSize Class Name
03c87ecc 3502 112064 mshtml.HTMLInputTextElementEvents2_onclickEventHandler
06c2aac0 570 9120 mshtml.HTMLInputElementClass

This is a bad figure, because it is never going down, even if we Garbage Collect. With just a few minutes of use of Internet Explorer, there is a huge number of event handles. The reason being because we never unwire the event handler, thus we are leaking events. We need to unwire them. Many people have bemoaned this problem in .NET: event subscriptions increment the reference count. Many people have written Framework wrappers for events to use “Weak Events”, or events that don’t increment the reference count. Both strong and weak reference have their advantages.

I’ve found the best way to do this is to keep a running Dictionary of all the events you subscribed to, and unwire them in BeforeNavigate2 by looping through the dictionary, then removing the element from the dictionary, allowing it to be garbage collected.

Here is my final code for unwiring events:

   1: [ComVisible(true),

   2: Guid("9AB12757-BDAF-4F9A-8DE8-413C3615590C"),

   3: ClassInterface(ClassInterfaceType.None)]

   4: public class BHO : IObjectWithSite

   5: {

   6:     private object _pUnkSite;

   7:     private IWebBrowser2 _webBrowser2;

   8:     private DWebBrowserEvents2_Event _webBrowser2Events;

   9:     private readonly Dictionary

  10:         <

  11:             HTMLInputTextElementEvents2_onclickEventHandler,

  12:             HTMLInputTextElementEvents2_Event

  13:         > _wiredEvents

  14:         = new Dictionary

  15:         <

  16:             HTMLInputTextElementEvents2_onclickEventHandler,

  17:             HTMLInputTextElementEvents2_Event

  18:         >();

  19:  

  20:     public int SetSite(object pUnkSite)

  21:     {

  22:         if (pUnkSite != null)

  23:         {

  24:             _pUnkSite = pUnkSite;

  25:             _webBrowser2 = (IWebBrowser2)pUnkSite;

  26:             _webBrowser2Events = (DWebBrowserEvents2_Event)pUnkSite;

  27:             _webBrowser2Events.DocumentComplete += _webBrowser2Events_DocumentComplete;

  28:             _webBrowser2Events.BeforeNavigate2 += _webBrowser2Events_BeforeNavigate2;

  29:         }

  30:         else

  31:         {

  32:             _webBrowser2Events.DocumentComplete -= _webBrowser2Events_DocumentComplete;

  33:             _webBrowser2Events.BeforeNavigate2 -= _webBrowser2Events_BeforeNavigate2;

  34:             _pUnkSite = null;

  35:         }

  36:         return 0;

  37:     }

  38:  

  39:     void _webBrowser2Events_BeforeNavigate2(object pDisp, ref object URL, ref object Flags,

  40:         ref object TargetFrameName, ref object PostData, ref object Headers, ref bool Cancel)

  41:     {

  42:         foreach (var wiredEvent in _wiredEvents)

  43:         {

  44:             wiredEvent.Value.onclick -= wiredEvent.Key;

  45:         }

  46:         _wiredEvents.Clear();

  47:     }

  48:  

  49:     void _webBrowser2Events_DocumentComplete(object pDisp, ref object URL)

  50:     {

  51:         HTMLDocument document = _webBrowser2.Document;

  52:         var inputElements = from element in document.getElementsByTagName("input").Cast<HTMLInputElement>()

  53:                             select new { Class = element, Interface = (HTMLInputTextElementEvents2_Event)element };

  54:         foreach (var inputElement in inputElements)

  55:         {

  56:             HTMLInputTextElementEvents2_onclickEventHandler interfaceOnOnclick = inputElement_Click;

  57:             inputElement.Interface.onclick += interfaceOnOnclick;

  58:             _wiredEvents.Add(interfaceOnOnclick, inputElement.Interface);

  59:         }

  60:     }

  61:  

  62:     static bool inputElement_Click(IHTMLEventObj htmlEventObj)

  63:     {

  64:         htmlEventObj.cancelBubble = true;

  65:         MessageBox.Show("You clicked an input!");

  66:         return false;

  67:     }

  68:  

  69:     public int GetSite(ref Guid riid, out IntPtr ppvSite)

  70:     {

  71:         var pUnk = Marshal.GetIUnknownForObject(_pUnkSite);

  72:         try

  73:         {

  74:             return Marshal.QueryInterface(pUnk, ref riid, out ppvSite);

  75:         }

  76:         finally

  77:         {

  78:             Marshal.Release(pUnk);

  79:         }

  80:     }

  81: }

After performing the same level of stress as before, there were only 209 instances of HTMLInputTextElementEvents2_onclickEventHandler. That is still a bit high, but it’s because the Garbage Collector done it’s cleanup. The Garbage Collector makes it a bit subjective to counting how many objects are in memory. If we really cared we could check and see which of those have a reference count greater than zero to get the full result, but I think that’s above the call of duty, right?

There are alternative ways to wire events. If the strong typing and plethora of interfaces is getting to you, it’s possible to use attachEvent and detachEvent albeit it requires converting these events into objects that COM can understand.

Part 3 we will look into manipulating the DOM.

Writing a Managed Internet Explorer Extension: Part 1

I’ve recently had the pleasure of writing an Internet Explorer add on. I found this to somewhat difficult for a few reasons and decided to document my findings here.

Managed vs Native

One difficult decision I had to make even before I had to write a single line of code was what do I write it with? I am a C# developer, and would prefer to stay in that world if possible. However, this add-on had the intention of being use commercially, and couldn’t make the decision solely based on preference.

Add-on’s to Internet Explorer are called Browser Helper Objects, often documented as BHOs as well. They are COM types, thus if we were going to do this managed, we will be doing some COM Interop. I’ve done this before, but mostly from a level of tinkering or deciding to go back to native. The .NET Framework had another benefit to me, and that was WPF. My BHO requires an user interface, and doing that natively isn’t as easy or elegant as using native libraries. Ultimately I decided to go with .NET Framework 4.0, and I can only recommend the .NET Framework 4.

Previous versions of the CLR has a serious drawback when exposing the types to COM: They always used the latest version of the CLR on the machine. If you wrote a BHO in the .NET Framework 1.1, and 2.0 was installed, it would load the assembly using the .NET Framework 2.0. This can lead to unexpected behavior. Starting in the .NET Framework 4, COM Visible types are guaranteed to run against the CLR they were compile with.

The Basics of COM and IE

Internet Explorer uses COM as it’s means of extending its functionality. Using .NET, we can create managed types and expose them to COM and Internet Explorer would be non-the-wiser. COM heavily uses Interfaces to provide functionality. Our BHO will be a single class that implements a COM interface. Let’s start by making a single C# Class Library in Visual Studio. Before we can start writing code, we need to let the compiler know we will be generating COM types. This is done by setting the “Register Assembly for COM Interop” in our project settings on the “Build” tab. While you are on the Build tab, change the Platform target to “x86” as we will only be dealing with 32-bit IE if you are running a 64-bit OS. Now that’s out of the way, let’s make our first class. We’ll call our class BHO.

   1: namespace IeAddOnDemo

   2: {

   3:     public class BHO

   4:     {

   5:     }

   6: }

By itself, this class is not useful at all, and nor can COM do anything with it. We need to let COM know this type is useful to it with a few key attributes. The first is ComVisibleAttribute(true). This attribute does exactly what it looks like. The next is GuidAttribute. This is important because all COM types have a unique GUID. This must be unique per-type per application. Just make your own in Visual Studio by clicking “Tools” and “Create GUID”. Finally there is the ClassInterfaceAttribute which will be set to None. Optionally, you can set the ProgIdAttribute if you want. This allows you to specify your own named identifier that will be used when the COM type is registered. Otherwise it’s your class name. Here is what my class looks like now:

   1: [ComVisible(true),

   2: Guid("9AB12757-BDAF-4F9A-8DE8-413C3615590C"),

   3: ClassInterface(ClassInterfaceType.None)]

   4: public class BHO

   5: {

   6: }

So now our type can be registered, but it isn’t useful to IE. Our class needs to implement an interface that IE always expects all BHO’s to implement: IObjectWithSite. This is an already existing COM interface that we will re-define in managed code, but will let the CLR know it’s actually a COM interface through a series of attributes.

   1: [ComImport,

   2: Guid("FC4801A3-2BA9-11CF-A229-00AA003D7352"),

   3: InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]

   4: public interface IObjectWithSite

   5: {

   6:     [PreserveSig]

   7:     int SetSite([In, MarshalAs(UnmanagedType.IUnknown)]object pUnkSite);

   8:     [PreserveSig]

   9:     int GetSite(ref Guid riid, out IntPtr ppvSite);

  10: }

This you can directly copy and paste into your project. Make sure you don’t change the GUID, either. This GUID is already defined by Internet Explorer. The ComImport attribute indicates that this interface is a COM interface. The InterfaceTypeAttribute is important. All COM interfaces we will be working is is InterfaceIsIUnknown. All COM interfaces implement a basic interface. In this case, IObjectWithSite implement IUnknown. We won’t actually be doing anything with this interface though that .NET can’t already do for us with the help of the Marshal class.

The GetSite and SetSite methods will be automatically called by Internet Explorer, we just need to provide the implementation. SetSite is of the most interest to us. pUnkSite is will be another IUnknown interface. Since we won’t be using the IUnknown interface, we’ll just use object instead and be happy with that. We’ll add the IObjectWithSite to our BHO class.

Before that, we need to add a few references to our project. Bring up the Add Reference dialog, and switch over to the COM tab. We’ll be adding these:

  • Microsoft HTML Object Library
  • Microsoft Internet Controls

.NET will automatically generate the .NET wrappers for us rather than having to declare all of them by hand like we did with IObjectWithSite. These libraries contain the useful parts of Internet Explorer that allow us to do cool things, like manipulate the Document Object Model of a page. Now let’s add our interface.

   1: [ComVisible(true),

   2: Guid("9AB12757-BDAF-4F9A-8DE8-413C3615590C"),

   3: ClassInterface(ClassInterfaceType.None)]

   4: public class BHO : IObjectWithSite

   5: {

   6:     private object _pUnkSite;

   7:     public int SetSite(object pUnkSite)

   8:     {

   9:         _pUnkSite = pUnkSite;

  10:         return 0;

  11:     }

  12:  

  13:     public int GetSite(ref Guid riid, out IntPtr ppvSite)

  14:     {

  15:         var pUnk = Marshal.GetIUnknownForObject(_pUnkSite);

  16:         try

  17:         {

  18:             return Marshal.QueryInterface(pUnk, ref riid, out ppvSite);

  19:         }

  20:         finally

  21:         {

  22:             Marshal.Release(pUnk);

  23:         }

  24:     }

  25: }

The GetSite is a fairly vanilla implementation that can be used for all BHOs. Internet Explorer will call GetSite with an interface GUID. We defer this back to our object from SetSite. SetSite gives us an object, but it isn’t just any plain ‘ol boring object. It actually implements a bunch of cool interfaces, like IWebBrowser2 and DWebBrowserEvents2_Event. SetSite is usually called twice by IE. When pUnkSite is not null, it’s an object that is IE itself. When pUnkSite is null, it means IE is shutting down and we need to our cleanup. We can cast our pUnkSite to those two interfaces which are in the COM libraries we referenced earlier. The return value in this case should always be S_OK, or 0. With IWebBrowser2 we can manipulate the DOM, and DWebBrowserEvents2_Event we can listen for events. Let’s add some simple functionality: whenever a page is loaded, let’s display a message box with the title. Here is what my final code looks like:

   1: using System;

   2: using System.Runtime.InteropServices;

   3: using System.Windows;

   4: using mshtml;

   5: using SHDocVw;

   6:  

   7: namespace IeAddOnDemo

   8: {

   9:     [ComVisible(true),

  10:     Guid("9AB12757-BDAF-4F9A-8DE8-413C3615590C"),

  11:     ClassInterface(ClassInterfaceType.None)]

  12:     public class BHO : IObjectWithSite

  13:     {

  14:         private object _pUnkSite;

  15:         private IWebBrowser2 _webBrowser2;

  16:         private DWebBrowserEvents2_Event _webBrowser2Events;

  17:         public int SetSite(object pUnkSite)

  18:         {

  19:             if (pUnkSite != null)

  20:             {

  21:                 _pUnkSite = pUnkSite;

  22:                 _webBrowser2 = (IWebBrowser2)pUnkSite;

  23:                 _webBrowser2Events = (DWebBrowserEvents2_Event)pUnkSite;

  24:                 _webBrowser2Events.DocumentComplete += _webBrowser2Events_DocumentComplete;

  25:             }

  26:             else

  27:             {

  28:                 _webBrowser2Events.DocumentComplete -= _webBrowser2Events_DocumentComplete;

  29:                 _pUnkSite = null;

  30:             }

  31:             return 0;

  32:         }

  33:  

  34:         void _webBrowser2Events_DocumentComplete(object pDisp, ref object URL)

  35:         {

  36:             HTMLDocument messageBoxText = _webBrowser2.Document;

  37:             MessageBox.Show(messageBoxText.title);

  38:         }

  39:  

  40:         public int GetSite(ref Guid riid, out IntPtr ppvSite)

  41:         {

  42:             var pUnk = Marshal.GetIUnknownForObject(_pUnkSite);

  43:             try

  44:             {

  45:                 return Marshal.QueryInterface(pUnk, ref riid, out ppvSite);

  46:             }

  47:             finally

  48:             {

  49:                 Marshal.Release(pUnk);

  50:             }

  51:         }

  52:     }

  53: }

Notice that in SetSite, if pUnkSite is null, I remove the event wireup. This is required, otherwise pUnkSite won’t get released properly and IE is likely to crash when a user tries to close it.

Registering

We have our code now, but how do we get IE to do anything with the assembly? First we need to register it. The .NET Framework comes with a tool called regasm. This will register our .NET Assembly like it were a COM library. Before we can do that, we need to add a strong name to our assembly. If you don’t strong name sign the assembly, the regasm is going to complain.

What you will want to do now is open the Visual Studio Command Prompt found in your start menu along with Visual Studio. You’ll want to run it as an administrator, too and change your working directory to your project’s output. Then call regasm like this:

   1: regasm.exe /register /codebase IeAddOnDemo.dll

If all goes well, you will see “Types registered successfully”. Let’s verify. Open up your registry by running regedit.exe and looking under HKEY_CLASSES_ROOT\CLSID. Remember the GUID you used in the attribute for BHO? It should be under there. In my example, I should see HKEY_CLASSES_ROOT\CLSID\{9AB12757-BDAF-4F9A-8DE8-413C3615590C}. Low and behold, I do.

Note that if you are using a 64-bit operating system, you will see it under HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{your guid here}. That’s OK and expected.

We have the COM class registered, but IE still doesn’t know it’s there. We need to add it to the list of BHO’s. They live under this key:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\Browser Helper Objects

or this for x64 machines:

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\explorer\Browser Helper Objects

Note that they key “Browser Helper Objects” may not exist if there has never been a BHO installed on the machine. If it’s not there, go ahead and create it.

Finally, create a sub key under the “Browser Helper Objects” using the same GUID that was registered. Make sure to include the curly braces like you saw earlier under CLSID. So now I have a key path:

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\explorer\Browser Helper Objects\{9AB12757-BDAF-4F9A-8DE8-413C3615590C}

If you want, you can set the default value of the key to a string of your choice to make it more identifiable in the registry. Lastly, you will want to create a DWORD under the registry called NoExplorer with a value of 1. This stops Windows Explorer from loading the Add On and limiting it to just Internet Explorer. I haven’t tested my add on with Windows Explorer so I have no idea if this procedure works for it. Now go ahead and start IE, and if all went according to plan you will see this:

bingalert

Looking under “Tools” and “Manage Add Ons” we see our BHO is listed there.

 

If you want to unregister your add on, simply do the following:

  1. Delete your BHO registration. In my case it’s key “HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\explorer\Browser Helper Objects\{9AB12757-BDAF-4F9A-8DE8-413C3615590C}”
  2. Call regasm like we did before, except use /unregister rather than /register.

The next blog post will cover listening to DOM events, such as when a button is clicked.

Vitamin D, Sunshine, and Rainbows

Today is the start of me coming off of a client project that I have been on and off for years now. I really like the project and I tend to jump in when the client is planning some large items over the coming months. Sadly that reign has come to an end, and it’s off to different things. Regardless, a somewhat unique thing that we do as often as possible is work at the client’s location, their building, their office. While this provides enormous benefits when working with the client, it does mean that we take what we can get when it comes to office space. Recently, they gave us a new room when they expanded to the floor below them. It’s a nice room with great tables and layout for pair programming, comfy chairs, and good development machines. Though the joke around the office is we’re going to suffer from a Vitamin D deficiency. Why? It’s because there is no window or place that natural light gets in.

Today I had the good fortune of working next to a window in our office.

I never really considered how much of a difference it makes, I always wanted to think “lamps are good enough”. Honestly though, I found myself having more energy and being much perkier. I’m not usually one known to be “perky” either.

So I want opinions, how important is a window in the office to you?

Retargeting Assemblies for SQLite

If you read my previous post about dealing with SQLite and SQL CE, then you know that I am on a mission to get unit tests working correctly with a SQLite database. I decided to see if I could get “assembly retargeting” working. First, what is “retargeting”?

.NET allows assemblies to be “redirected” in a sense when the platform is different. This is how the .NET Compact Framework actually works. All .NET CF assemblies are retargetable. In your application, you are referencing assemblies such as System.dll. However, ever notice that .NET Compact Framework application will run on a desktop? That’s because the runtime is silently retargeting them to the full assemblies, like System. This is key to getting the application working on different platforms. Under all of the covers, .NET relies heavily on platform invoke to do the heavy lifting in the WinForms world. However, the P/Invoke calls between a Windows Mobile application and a workstation are wildly different. This is why redirection is needed to work on multiple assemblies.

How can we get this to work for SQLite?

There are a few things you need to get retargeting to work. You need two assemblies, one for the Compact Framework and one for the full framework. I am using the managed wrapper provided by PHX software, which happens to be open source as well. They have ZIPs of both full and compact assemblies.

The second catch is they need to have the same strong name. Oddly, these two managed wrappers use a different strong name key depending on the platform, but since it’s open source it’s very easy to download, synchronize the keys, and recompile.

The final bit is putting the assemblies in a place the application will look for them without causing problems. In your project, add a reference to the Compact assembly. Leave this for now.

Open a command prompt with administrative permissions and use gacutil to install the Full one.

Finally, you need to make sure the unmanaged libraries are ending up in your project output directory for both compact and full. There are 5 files:

  • System.Data.SQLite.exp
  • System.Data.SQLite.lib
  • SQLite.Interop.065.lib
  • SQLite.Interop.065.exp
  • SQLite.Interop.065.dll

All of these are needed. The best bet is to include them in your project with compile action to “None” and “”Copy if newer” for copying.

All things done right…Tada! It works, but what is really going on? What we are really doing is tricking the .NET Framework into loading the full framework assembly instead of the compact assembly. This has to do with how assemblies are resolved. Without going over a lot of details, assemblies are resolved in this order:

  1. The GAC
  2. The probe path of the current application as specified by the AppDomain (like the bin)

That’s the very brief description, and a bit simplified. For the real down-and-dirty, check out this MSDN article. When the runtime looks in the GAC, it finds the assembly we installed. The assembly hash isn’t the same, but since it was retargetable and the strong name keys matched, it loaded it from the GAC anyway and didn’t even bother looking at the one we actually referenced. When it is deployed to a mobile device, it is not in the GAC, so it loads the one we referenced which should have been copied to the output. For the platform invoke, since we copied both the Win32 and the ARM libraries, platform invoke resolved their location from the deployed directory.

Neat stuff, but seemed harder than it could have been.

For what it’s worth, this is also how SQL CE is working. However it does it’s magic by putting the assembly in the GAC for the desktop version and the compact version.

C# and VB.NET

I don’t like to think of myself as a “C# Developer”. I prefer to think of myself as a “.NET Developer”, mainly because C# is not the only tool I know how to use. However, I primarily use C# as a language of choice because that’s the majority of what most .NET developers use, and I never had a reason to know or understand VB.NET. It’s not to say I was clueless about VB.NET, I’ve glanced at the syntax and I felt that it was similar enough to C# that I didn’t really need to get into the dirty details. Nor do I want to spend the time on comparing the two. There have been countless blog posts on that already.

Now, I chose to write a Compact Framework Application in VB.NET. Some of my co-workers looked at me like I was nuts. That’s one thing I really never got: Why does the general C# crowd hate VB.NET? When I say hate, I mean hate. That’s at least the sense I get from them. I’ve asked, and one such answer was “It’s too wordy and verbose,” but does that really qualify hating it?

Now I ask those C# developers, can you, with a pen and a paper and no references, write a property in VB.NET? How about class with a generic? Maybe a generic with multiple type constraints? Subscribing to an event? You can see where I am going with this. I had an experience where I did a one day bit of consulting to someone helping them port a .NET 1.1 application to the latest stuff. It went smoother than they expected once I explained the difference between a “Web Site” and a “Web Application”. They had some time to ask me, “What’s new in VB.NET?”

Ulp.

Well, there is generics of course, but I struggled to remember syntaxes. Simple things, like a property. Also, the editor for VB.NET is slightly different with how it handles intellisense completion, so I botched that a few times. It was rather embarrassing. I decided at that time there was zero excuse for me, as a consultant, to not know VB.NET as well as I know C#. To do this the most effective way possible, I needed to write a real application in it.

I’m comfortable with VB.NET, in fact I have some preferences over C# with it. I am really looking forward to the VB.NET “catch-up” for VB 10. One thing I noticed that C# had a lot of syntactic sugar that VB.NET lacked, like auto properties. The killer for me right now is that a lambda expression must be a Function instead of a Sub.

I have a new appreciation for VB.NET and VB.NET developers. Maybe I’ll do a code camp or user group presentation in it once :-).

Nastiness with Data in the .NET Compact Framework and the spiral downward (With Updates!)

The First Try

As of late, if you for whatever reason on earth follow me on twitter, you might’ve picked up on the fact that I am working on a Windows Mobile project. Written in VB.NET. Here is my experience with working with a SQL database in the .NET Compact framework, and hopefully save someone some headaches.

I naturally wanted to use SQL CE for a database. It comes out of the box, right? It’s made by the SQL team, which I usually have no problems with. SQL Server has been a solid Microsoft product for years, so let’s use that, I mean Visual Studio was “nice” enough to install it for me.

Well, on a previous application we used SQL CE. And it immediately crashed the application when we ran a query. Let’s look at some numbers. The database contained 2 tables, and one foreign keyed to the other. TableA contained roughly 45,000 records, and TableB contained roughly 4 million records. TableB contained a foreign key to tableA. The database would be read only. No inserts, updates, deletes – nothing of the sort. TableA contained 2 columns, an identity column and a nvarchar lookup that was 250 characters wide (500 bytes for those counting). TableB contained an identity, a reference to TableA, and 5 nvarchar columns that were also 250 characters.

Not the most complex of datasets. Also note that I am generalizing names of tables and columns to avoid being specific about a project.

Now, the device was admittedly underpowered. We’re talking a 250 MHz ARM processor with 16 MB of RAM. 8 MB of the RAM was being used by the OS and a Bluetooth loader. We have 8 MB of memory. Not exactly a ton, right?

SQL CE 3.5 flat out out ran out of memory. Poof, gone. Well, we tweaked some settings in the connection string like pool size, max working set, etc. No dice. The database was already given to me the way it was, was TableA really needed? Let’s merge the two tables – the thought being that the JOIN was killing it. Nope, didn’t make things better. What was the query? A lookup by an ID:

   1: SELECT Id, FirstName, LastName, Location FROM TableB WHERE LookupId = @LookupId

Yeah, pretty simple. It should only return one record, too. It was accessed using a SqlDataReader to avoid the overhead of datasets, LINQ, etc. Here’s where things got interesting. LookupId was an nvarchar, but was really just a set of numbers, like 3487982429803758. Too big to be an Int, but small enough to be a BigInt. The data had leading zeros in them, but regardless the number would be unique. So let’s change it to a BigInt. Though after the conversion, it just really didn’t look right. Some numbers were flat out converted wrong. Here is how I converted it:

   1: ALTER TABLE [TableB]

   2:     ADD [LookupIdTemp] BIGINT NULL

   3: GO

   4: UPDATE [TableB] SET [LookupIdTemp] = CONVERT(BIGINT, [LookupId])

   5: GO

   6: ALTER TABLE [TableB]

   7:     DROP COLUMN [LookupId]

   8: GO

   9: ALTER TABLE [TableB]

  10:     ADD [LookupId] BIGINT NULL

  11: GO

  12: UPDATE [TableB] SET [LookupId] = [LookupIdTemp]

  13: GO

  14: ALTER TABLE [TableB]

  15:     DROP COLUMN [LookupIdTemp]

  16: GO

  17: ALTER TABLE [TableA]

  18:     ALTER COLUMN [LookupId] BIGINT NOT NULL

  19: GO

Not very fast, but I was able to execute it from a desktop machine and let it run for a few minutes (sp_rename doesn’t exist for SQL CE for those unaware). I tried it on some sample data, and it seemed to work fine. No so. Here is a sample input, and sample output of where things didn’t work and did work. See if you notice a pattern.

Input Output
5 5
10 10
150 150
1000 1000
05 0
050 5
001000 10
010 1
0000000010 0

See a pattern emerging? It’s the leading zeros. SQL CE does not convert a string to a BIGINT correctly if it has leading zeros. Simple as that. For every insignificant zero on the left, it chomps that many digits from the right.

Input Output
5 5
10 10
150 150
1000 1000
05 5
050 50
001000 1000
010 10
0000000010 00000001

See now? That’s pretty pathetic. As expected, if you do this with a full SQL engine, it works as expected. Also, it only seems to do this for a BIGINT. The conversion works just fine for INT, SMALLINT, TINYINT, you name it. I got in touch with the SQL CE team, and acknowledged the bug. They were also kind enough to provide a recent work around: put a sign in front of the digit. i.e. “+050” is properly converted to 50.

Even after all of that the query flat out didn’t run on the device. We started to worry that the device just wasn’t powerful enough. So we tested it on a slightly better device, a 550 MHz XScale with 64 MB of RAM. It ran, but was slow. Painfully slow. Now we really started getting worried.  Was our goal just too much for a CE device for a .NET Application? Were we going to have to move to something native like C++ eMbedded?

Well, what about indexes? Did we have our LookupId column indexed? You bet. That’s the first thing you do on a lookup column with a ton of rows. It didn’t seem to help much either. Usually indexes perform miracles. So what were we doing wrong?

We went through all sorts of suggestions, like using the Seek operation,  downgrading to SQL CE 2.0 (you never know) any nothing worked.

Well, let’s try SQLite first at the suggestion of a hardware vendor. We found a nice app on CodePlex that even converted it for us. SQLite is a native implementation, and we had a tricky time finding a managed wrapper for it, but we did here. the conversion took ages, but the managed wrapper was very clean and written in ADO.NET style. Let’s give it a try.

It came back instantly. I thought something went wrong or the conversion didn’t convert all of the rows. A count check confirmed that all of the millions of rows were there and came back. Though I tried a LookupId of “3”. What happens when I try to use 10000000000000?

Came back 10 seconds later. Ah but wait – we didn’t get the index pulled over. Let’s add the index in try again. The size of the database grew significantly, from 400 MB to 750 MB. The database was on a compact flash card, so we weren’t too concerned about that. Aside from the size, the query was always returning instantly (or at least sub-second).

I don’t know exactly why SQLite is infinitely better, but I think indexes are implemented in a strange way – the size of the database didn’t seem to change much in SQL CE without the index. If anyone wants to tell me how indexes work in SQL CE, I’d be happy to know. Or possibly tell me if I was doing something wrong. Some back research seemed to affirm my point with SQL CE. It’s very bloated for the Compact Framework.

One last little annoyance – the SQL CE Framework is not as simple as including an assembly in your project. It requires a full CAB installation. That isn’t exactly correct. A SQL CE MVP was kind enough to tell me that it can be distributed with the application. However, I was not able to find anywhere on MSDN, or much other places on the internet, on how to do that. What files do I need? Where do I get them? Microsoft wants you to use the MSI so they can update it with Windows Updates.

That left me with a pretty bad taste in my mouth for SQL CE. Given that the only difference between SQL CE and SQLite were a few key class names, I didn’t think I ever wanted to use SQL CE again.

The Second Try

I later was working on a project that needed to persist cached data somewhere. This is for a WPF-based Desktop application. SQLite came to mind, but it had one flaw that annoyed me. Different assemblies for x86 and x86-64. I could either have two different builds, or latebind, or do something clever, but I thought it was time to try SQL CE again, I don’t plan on having more than 100 rows in any given table anyway. SQL CE isn’t much better either. Again, it isn’t redistributable in any form other than an MSI. At least the documentation doesn’t recommend it. But at least I had the freedom to just distribute the SQL CE MSI for the correct platform and the app would just work.

You might be thinking, “Why not just force your application to run in x86 all the time and avoid the x86-64 problem?” Well, I am a 64-bit junky. If it’s a managed app, it better well damn work on the OS’s preferred architecture. It’s a pet peeve of mine I suppose. Unless there is a good reason to force x86 (like performance problems with 64-bit – remember the x86-64 Jitter is slower) or moving a legacy application to at least work properly on x86-64 as a starting point.

SQL CE is actually an OK database platform for a desktop. There are some quirky bugs with it, like the issue with BIGINT, but not bad. I suspect it will improve as well. However I will never use it for a Compact Application ever again, regardless of how big I think the data is going to be.

Unit Testing Insanity

Now, I do unit testing whenever I can. Sometimes the client prefers I don’t due to time constraints or don’t see the benefit. I push for it, but there is only so much I can push. The previous client was one of those, so I didn’t notice this issue until recently.

If you’ve never written a Compact Framework application, there are a few things to know. First, an application targeted for the Compact Framework is fully capable of being referenced by Full .NET Framework assemblies, which makes sense. the stranger one is that in a Compact Framework project you can reference a Full .NET Framework assembly, it just may not run properly. That in mind, here is a sample solution setup.

  • ProjectFoo.Data
  • ProjectFoo.Business
  • UnitTests.ProjectFoo.Data
  • UnitTests.ProjectFoo.Business

All of them are Compact Framework Class Libraries in VB.NET. The UnitTests projects need to be Compact Framework projects because for whatever reason, a Full VB.NET Project cannot reference a Compact Framework VB.NET Project (though C# can do it). My reasons for using VB.NET is worth another blog post later.

Regardless, things were going smoothly with this approach. NUnit has no problem working with the Compact Framework, my CI server runs the tests fine, Resharper runs them, seems like a good setup. Time to add some data! I went for SQLite since this is a Compact Framework application. I referenced it, added all of the native dependencies, at this point it’s like clockwork for me. My first test is to test a class that creates the database for the first time. Here was my result:

System.IO.FileLoadException: Could not load file or assembly ‘System.Data.SQLite, Version=1.0.65.0, Culture=neutral, PublicKeyToken=1fdb50b1b62b4c84, Retargetable=Yes’ or one of its dependencies. The given assembly name or codebase was invalid.

Huh? Lets check the Bin directory and make sure it’s there. Yep.

I knew this wasn’t going to be an easy one. I’ve dealt with some bad problems with loading assemblies in the past when I was writing an multi AppDomain project in the past, and knew the best place to start was the Fusion log. It happened to already be enabled from previous problems like this, which is lucky, I guess. Here’s what was in the log:

*** Assembly Binder Log Entry  (9/28/2009 @ 12:33:27 AM) ***

The operation failed.
Bind result: hr = 0x80131047. No description available.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
Running under executable  C:\Program Files (x86)\JetBrains\ReSharper\v4.5\Bin\JetBrains.ReSharper.TaskRunner.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: User = VCSJONESDC\Kevin Jones
LOG: DisplayName = System.Data.SQLite, Version=1.0.65.0, Culture=neutral, PublicKeyToken=1fdb50b1b62b4c84, Retargetable=Yes
 (Fully-specified)
LOG: Appbase = file:///C:/development/Thycotic/SecretServer.WindowsMobile/trunk/UnitTests.SecretServer.WindowsMobile.Data/bin/Debug
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = UnitTests.SecretServer.WindowsMobile.Data
Calling assembly : SecretServer.WindowsMobile.Data, Version=1.0.3557.41832, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: No application configuration file found.
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v2.0.50727\config\machine.config.
ERR: Failed to apply policy (hr = 0x80131047).
ERR: Unrecoverable error occurred during pre-download check (hr = 0x80131047).

Not too much help. The HR is the same as the except which I doubled checked like so:

   1: [System.Runtime.InteropServices.Marshal]::ThrowExceptionForHR(0x80131047)

Yes, I used a PowerShell script to test this out. Once you get use to it, I find it very handy for such uses.

So, let’s start digging into the IL of SQLite using our good friend ILDASM. Reflector doesn’t quite show one of the inner workings we want. If you look at the manifest of SQLite, it’s got this oddball in there:

   1: .module extern 'SQLite.Interop.065.DLL'

Reflector simply shows it as a comment at the top of the manifest disassembly. So what on earth is SQL.Interop.065.DLL?

Other assemblies contain the same .module reference, and ironically they can point to unmanaged libraries, like kernel32.dll. This is not the same as the unpopular .NET Modules. A .NET Module is, in short words, an assembly without a manifest. So what is SQL.Interop.065.DLL? It’s not a valid Win32 PE or PE+ file either. A look at it with the Dependency Finder tells us it’s compiled for the ARM architecture and it’s Subsystem is Windows CE 2.0+.

Here’s a screen grab.

ss

For those curious, coredll.dll is the equivalent of kernel32.dll on a Windows CE system, but since I am using Windows 7 it obviously won’t be found on my desktop.

What does this mean? Why is a managed assmebly referencing an unmanaged assembly? Well, it isn’t exactly a “reference”. What happens is, whenever you do platform invoke on a DLL, the DLL is added as a “module” in the manifest. Let’s try it. Make a new C# project and throw in this code:

   1: [DllImport("user32.dll")]

   2: [return: MarshalAs(UnmanagedType.LPTStr)]

   3: internal static extern string CharUpper

   4:     (

   5:         [In, MarshalAs(UnmanagedType.LPTStr)] string lpsz

   6:     );

Now look at our manifest in ILDASM, we have this:

   1: .module extern user32.dll

I’m not entirely sure why it was quoted in the first example and not in the second, or if it has any significance.

The reason at this point is pretty obvious: We’re trying to do Platform Invoke on a library that my processor is incapable of handling.

Makes sense, but I am a bit bummed. Is SQL CE a better solution? Since SQLite is retargetable, can I use different versions of SQLite depending on the platform? I’m not sure right now, but I’ll be sure to follow up. If you have a working solution for this I would love to hear it, too.

Thoughts on Office 2010 Technical Preview

While it was leaked out via a Bit Torrent, as most things do these days (erm, Windows 7 “RTM” as people are calling it) I decided to wait until it was available to me under Microsoft Connect. Yesterday I got the chance to download and install it. I wanted to share my thoughts on it as well.

Native x64

Yes, Office 2010 is now available to run x64. This surprised me actually, that’s a huge undertaking and I didn’t immediately see the benefit. However, people that spend their lives doing analysis and equations in Excel that have a x64 operating system will appreciate this. One thing that does interest me is how this affects 3rd party tools. 64-bit program cannot load a 32-bit library, and Office is inherently  COM oriented, so I wonder how this will play with plug ins. Most Virus Scanners offer some plug in for scanning a document as it is loaded. Macro enabled documents are a long time distributor of viruses. Office 2007 took a small bite out of that by changing the document’s extension for macro enabled documents as well as tightening the security.

Initially I was also worried about some of the downsides of x64 – mainly pointer bloat. Pointers in x64 are 8 bytes wide, as opposed to 4 for x86. This can lead to additional memory use by the application. I didn’t see any obvious memory issues in any of the office programs though.

Outlook 2010

outlook2010Right off the bat I noticed they fixed something that was really annoying, and that is if you use Outlook Anywhere (Exchange over HTTP) it would not remember your password. Every time Outlook started, you had to enter your password, no choice. My Windows password is pretty strong, so someone would have to get on my Windows account first before they could read my email. However since Outlook forced me to type my password every time, it also forced me to create a somewhat weak password – at least one that I could remember. Since we also have Outlook Web Access, someone could have gotten into my email through the web client if they guessed my password. Now I am able to keep my Exchange password pretty complex and only have specific devices – which are always secured with their own password – remember it.

Aside from that addition they also did a little interface revamping, specifically the addition of the Ribbon. It looks more polished, and allows you to access more features. It was something that also irritated me in Office 2007 was the inconsistent UI in the Office Suite. Notably, Outlook and Visio were missing the Ribbon – both of which have been fixed.

Outlook 2010 does a better job of organizing your email now as well, as opposed to a linear timeline of when it was received. It takes a bit of a GMail approach to it by grouping related emails into a conversation-like format. It was a bit confusing at first, so here is how I think it works. To the left of all grouped messages are an expanding arrow to allow you to view the conversation. Initially, it only shows ones that are in the inbox. If you click it again then it will include the entire conversation – ones from Sent Items and the Inbox. Once you get the swing of it it makes finding emails a lot easier. If you get a lot of emails in one day, you can catch up on what was already said much quicker. Of course, if the idea of grouping emails disgusts you, you can go back to the linear timeline – or any other sorting mechanism.

Something else that is new is the idea of Quick Steps – and I really like this. It allows you to create predefined actions on a message, say “Move to Folder Y and mark it as read”, or “Forward this to Tom and send it”. Here’s the kicker – you can assign keyboard shortcuts to it as well. My boss wanted this feature so badly he even took to writing a VBA Script that does it for him in Outlook 2007.

Maybe it’s me, but it appears that some basic artwork for icons are missing in the Technical Preview and are substituted with orange dots.

Small feature, but cool. Maybe it did this in 2007 – but I couldn’t find it – it now tells you how much of your space quota you are using. My quota is 500 MB and I am using 80. This probably only works for Exchange, possibly anything IMAP. I’ll have to test it.

Composing emails hasn’t changed that much. Aside from the ribbon, some other small features like “insert a screenshot” are now there as well.

Stability overall seems good. My mailbox isn’t huge so I can’t really vouch for how good it is with performance, but it is still a COM based application, and thus at the root a single threaded application. It didn’t hang on me though – I’ll update if it does.

One final thing, which I hope they fix, is that Outlook Today screen. That screen hasn’t changed since Outlook 97 or 2000, and it definitely feels worn and old with the new slick Ribbon UI. There’s so much potential with that screen but it isn’t being used.

Perhaps there are some additional things I missed. I can’t test it against Exchange 2010 so I don’t know if using Exchange 2010 will unlock some additional goodies.

Word 2010, Excel 2010, PowerPoint 2010

Word and Excel are important applications to many, and it is unfortunate for me to say, at least so far – I can’t see anything different. The UI is updated to the new Office Tab rather than the Button – but the look and feel are the same. Startup is lightning fast though – I don’t even have time to see the splash screen. It seems to work with large documents better and not get sluggish like it use to. Maybe that is a new found power of x64, but I like it!

I’ll keep playing around and update if I find anything new.

Updates

One of my buddies pointed out to me that Office 2010 has better support for the Open Office formats. Though 2007 also supported it, many people called it incomplete and often saved it in a way that only Office could open (thus defeating the point of being “Open”). I would be a little surprised, but not completely, if this is a 2010 feature and not also an update to 2007 by means of a Service Pack.

I’m not sure if it is because it is coming with Windows 7 or Office 2010, but there are a few new fonts as well.

ASP.NET AJAX 4.0 Client Template Rendering – The Observer Pattern – Part 2

We last left of here, http://msmvps.com/blogs/vcsjones/archive/2009/06/07/asp-net-ajax-4-0-client-template-rendering.aspx about a simple introduction to the AJAX 4.0 Client Template Rendering. I’d catch up on that one if you haven’t read it before you read this one.

Our last example was pretty simple. We took a chunk of JSON, and displayed it in an unordered list with simple JavaScript and the AJAX Framework. Let’s take this a step further with actually manipulating the data. The framework allows manipulation of data, and it does this with the observer pattern. The definition of the observer pattern is a bit weak, but Microsoft is advertising this as “a true implementation of the observer pattern” rather than a notification pattern that uses constructs like events. This leads a new chunk of the AJAX Framework, Sys.Observer. We need to understand how this pattern works before we can manipulate data.

This allows the AJAX Framework to watch, or “observe” for changes on certain things. This can be a DOM element or any JavaScript object. In our case, we are working with a collection. The simplest means of working with our collection is using the Sys.Observer.add() method. This allows you to add an item to a collection, and also notifies the AJAX Framework that the collection was changed. If the AJAX Framework has something bound to our collection, like our unordered list, it knows it needs to update the list.

Take a look at our observer in action:

   1: function add() {

   2:     Sys.Observer.add(descriptions, { Name: "Cat", Description: "Cats make a meowing sound" });

   3: }

This code sample is build on the code in the previous blog entry.

When we call “add()” we are adding a new item to the descriptions collection. Not only does this add it to the collection, it also notifies anything that is using the descriptions collection that it was notified, and the AJAX Framework correctly “rebinds” the UI, in our case the unordered list.

The Observer class has a lot of additional functionality that you would expect for manipulating a collection, remove, removeAt, etc. The full details are outlined in MSDN.

If we add an input element that calls our add function, we can see it in action:

   1: <input type="button" value="Do Add" onclick="add()" />

Neat huh? It took little effort to get us this far. The Observer pattern is pretty powerful as well. Using the observer pattern, we can also create our own listeners and react appropriately when the observer makes a change to an object, and it goes far beyond working with collections.

We’ll dig further into the observer pattern in the next post.

ASP.NET AJAX 4.0 Client Template Rendering

Recently I have been dabbling with the ASP.NET 4.0 Framework. There were two things that immediately got my interest:

  1. An actual new version of the CLR. More of come on that later…
  2. AJAX 4.0

Now, I didn’t mean to say I was pumped an excited about it, but a certain element caught my attention, and that is Client Rendering. This basically, allows you to bind simple HTML to JSON or a simple JavaScript array. The idea is interesting, but I wasn’t sure how well it’d play.

To get started, I looked here: http://www.asp.net/learn/whitepapers/aspnet40/. This is a brief overview of the application. Not entirely informative, but it got me started. I fired up Visual Studio 2010 and began to tinker.

To start off, I wanted to build something simple. No JSON or Web Service calls, basics! I’m a slow learner. So, what exactly am I doing with this? Given the scenario where I have a JSON object with some information on it, I want to display it on the page. Now, there have been a few ways of doing this in the past. jQuery makes UI component for adapting JSON objects to tables, ordered elements, etc. The Microsoft implementation isn’t too different. It defines a binding for some sort of repeatable HTML element. Tables have repeating rows, unordered lists have repeating list items, etc. Unordered lists are pretty simple, so lets start there. Let’s say we are given a JSON object similar to this:

   1: var descriptions = [{ Name: "Foo", Description: "Foo is a great product." }, { Name: "Bar", Description: "Bar is OK, but not great."}];

Pretty simple. We have 2 objects in a collection with a Name and Description property. Now we want to show them in in an unordered list.

First, to get started, we need a ScriptManager in our page. This wires in all of Microsoft’s AJAX scripts for us.

   1: <asp:ScriptManager ID="MyScriptManager" runat="server">

   2:     <Scripts>

   3:         <asp:ScriptReference  Name="MicrosoftAjaxTemplates.js" />

   4:     </Scripts>

   5: </asp:ScriptManager>

I just plopped this right after the <body> tag. Notice the script reference is simply working on name. One of the new features of AJAX 4.0 is Microsoft broke apart the AJAX Framework JavaScript into different libraries. This allows you to selectively use the libraries you need and avoid large chunks of script you don’t need. In this case, we need the AJAX Templates script.

The AJAX functionality we will be using is an attached DataView. The scripting library works by attaching itself it an HTML element. In our case, it’s a unordered list. The attachment is done with a simple attribute on the element, sys:attach and then specify what you want to attach. Before we can go there, we need to tell our browser certain things, mainly namespaces. The body tag will contain certain namespace imports, similar to how WPF XAML imports namespaces. We want the DataView and the Sys namespace, so we’ll throw those in there:

   1: <body xmlns:sys="javascript:Sys"

   2:     xmlns:dataview="javascript:Sys.UI.DataView"

   3:     sys:activate="*">

There is one additional attribute, sys:activate that is in there. We’ll touch on that in a bit. So now we want to build our unordered list and attach a dataview to it. Let’s put together our unordered list and go through it.

   1: <ul id="descriptionsList" sys:attach="dataview" class="sys-template" dataview:data="{{ descriptions }}"> 

   2:     <li>    

   3:         <h3>{{ Name }}</h3>

   4:         <div>{{ Description }}</div>

   5:         <hr />

   6:     </li>

   7: </ul>

Notice the sys:attach attribute, and we are attaching a dataview to our unordered list. The other important attribute is the dataview:data. This is basically what data you want to bind to. Finally, we have a list item that is our “template” for what AJAX should use for all of the items. We denote a binding using double curly braces {{ }}. So {{ Name }} binds to the Name attribute in our JSON, etc. The dataview:data attribute is bound to our descriptions JSON object we declared.

There is one little piece left, and that is the class of sys-template. This is just a minor styling that sets display: none. This hides the entire element and the AJAX Framework will set the display properly once it has done fully loading all of the data and rendered it.

All together, it renders something like this:

rendered

All together, this was all of my source:

   1: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ClientTemplates.aspx.cs" Inherits="DataControls.ClientTemplates" %>

   2: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

   3: <html xmlns="http://www.w3.org/1999/xhtml" >

   4: <head runat="server">

   5:     <style type="text/css">

   6:     .sys-template

   7:     {

   8:         display: none;

   9:     }

  10:     </style>

  11:     <script type="text/javascript">
   1:  

   2:         var descriptions = [{ Name: "Foo", Description: "Foo is a great product." }, { Name: "Bar", Description: "Bar is OK, but not great."}];

   3:     

</script>

  12: </head>

  13: <body xmlns:sys="javascript:Sys"

  14:     xmlns:dataview="javascript:Sys.UI.DataView"

  15:     sys:activate="*">

  16:     <form id="form1" runat="server">

  17:         <div>

  18:             <asp:ScriptManager ID="MyScriptManager" runat="server">

  19:                 <Scripts>

  20:                     <asp:ScriptReference  Name="MicrosoftAjaxTemplates.js" />

  21:                 </Scripts>

  22:             </asp:ScriptManager>

  23:             <ul id="descriptionsList" sys:attach="dataview" class="sys-template" dataview:data="{{ descriptions }}"> 

  24:                 <li>    

  25:                     <h3>{{ Name }}</h3>

  26:                     <div>{{ Description }}</div>

  27:                     <hr />

  28:                 </li>

  29:             </ul>

  30:         </div>

  31:     </form>

  32: </body>

  33: </html>

And I didn’t write a single line in the code-behind, either.

This demonstrates the basic functionality of the binding. Next we’ll look at how to actually bind to dynamic data from a web service call, and manipulating the data.

Update: If you want to download a Visual Studio 2010 project, I’ve uploaded it here: http://bit.ly/VCWFd. It looks like Community Server is blocking some of the code snippets thinking it is XSS.