次の方法で共有


Building your own data bound controls...

In any Web Application, you have data bound controls. The job of a data bound control is to transform your data from some arbitrary format into the format it probably should have originally been stored in. In fact, about 90% of computer code simply converts some chunk of data from one arbitrary format to another arbitrary format.

Let’s say I have some data in a SQL Server which is stored in some highly optimized format on a disk somewhere. I can read it, but I access it through a connection that gives me that data in some sort of ADO.net record set format. This is great because I can return that object from my web service. The web service will serialize the data into SOAP. SOAP is a human readable representation of data that usually won’t actually be read by any humans. SOAP is great because it’s all text based so it supports 7bit token ring networks, so you don’t have to worry about upgrading your network.

So anyway, on your web front end you’ll get the SOAP and the deserializer will parse this text and build the dataset you just had a second ago. Then, you bind that dataset to an ASP.Net DataGrid control of some sort. This article will eventually detail exactly why that is so nifty after I’m done with my rant.

When DataBind is called, the grid control will loop through your dataset using its IListSource implementation. Inside, it builds DataGridRow objects which basically store the same thing as a DataRow, but since the DataGrid isn’t necessarily binding to a DataView, it has to create its own row abstraction. The grid does this and is happy, and then later on the framework calls CreateControlHierarchy. This tells all the controls to convert their data into a bunch of System.Web.UI.Control objects because that’s what ASP.NET likes to have around.

So the grid loops through its collection of DataGridRow objects and creates a bunch of TableRow controls, each one of those having a bunch of TableCell objects depending on what columns are bound to your grid. So ASP.NET has in memory this massive tree of Control objects. What does it do with it? Well it calls a bunch of stuff on each item such as OnInit, OnLoad, and OnPreRender. Oh, and somewhere in there it loads and saves the ViewState from some massive serialized blob.

Eventually, ASP.NET calls Render on all of these controls, and all Render does is translate the data into HTML, which is simply yet another representation of that same data. HTML comes in handy because it’s human readable and easy to transmit over your 7bit token ring network. Also it’s flexible and easy to work with since the standard is bound to change (if HTML were binary, FrontPage sales would have skyrocketed.)

So this is all transmitted over a socket and dumped out to whoever happens to be listening. Ideally, this ends up being parsed by a web browser, such as Internet Explorer. Internet Explorer eats up this nice HTML and builds a DOM (Document Object Model) out of it. This is a binary representation of your control hierarchy so IE can work with it (computers don’t like human readable text because they think humans are weak and inefficient,) manipulate stuff quickly, and present an object model to anyone who cares to program against it (like all you scripters.)

IE will finally draw this data out on the screen in a matter that’s pleasing to the eyes (for those of us who wouldn’t be able to look at The Matrix in its encoded form.)

So this is nice and efficient, we only looped through the same data about 100 times and didn’t really change anything. So what’s my point with all this? I’m not sure, but it would be cool if the control hierarchy that ASP.NET creates was the same as the one that IE uses, and we could just transmit it directly over the wire. Maybe someone wants to write that ActiveX control and IIS extension? No, actually what I’m saying is that often instead of focusing on how to optimize your for loop, you should be focusing on whether that loop is needed in the first place. Ok, end of rant – onto point of article.

.NET Data Bound Controls

So ASP.Net, well – actually pretty much the entire .NET framework – is quite nifty in the fact that you can pass any control a “DataSource” and it somehow magically knows how to read it and use that to populate itself. So what is a DataSource object anyway? Well, let’s see – I can pass in a DataSet which is nice. If it’s a small chunk of data that I can hard code into my program, I can pass in an Array or an ArrayList. If I need a key value pair, I can pass in a Hashtable. All of these work, that’s cool! So I bet these data bound controls have a bunch of “if _datasource is Array then do this” and “if _datasource is Hashtable then do that” blocks. But wait, I can write my own class and implement IEnumerable or IListSource and that works too.

So how do these data controls work and what if I want to build my own? Well, I sat down and read through a bunch of ASP.NET source code such as the DataGrid, DropdownList, Repeater, etc. They’re all pretty much the same and work in the same way. So I wrote my own little control that takes a data source and just dumps out the items to the console. It looks something like this:

   class Program

   {

      static void Main(string[] args)

      {

         MyControl[] controls = { new MyControl(), new MyControl(), new MyControl() };

       controls[0].DataSource = new string[] { "One", "Two", "Three" };

         controls[0].Render();

       

         controls[1].DataSource = Data.GetHashTable();

         controls[1].DataField = "Value";

         controls[1].Render();

         controls[2].DataSource = Data.GetDataTable();

         controls[2].DataField = "Name";

         controls[2].Render();

      }

   }

As you can probably guess, Data.GetHashTable just “new”s up a hash table and returns that. Data.GetDataTable() creates a data set from an XML file and returns that. I won’t include the source to those things because it’s not all that exiting. So I run my program and it writes out to the console:

Writing object of type: System.String[]

One

Two

Three

Writing object of type: System.Collections.Hashtable

Fish

Cat

Dog

Writing object of type: System.Data.DataTable

Apple

Banana

Orange

Pretty amazing I think. So how does my control work? The same way all other .NET data bound controls work. First off, it supports binding two types of objects – IEnumerable and IListSource. IEnumerable would be a hash table, array, ArrayList, etc. An IListSource would be a DataSet, DataTable or DataView.

The first thing my binding code does is check if it’s an IEnumerable. If so, the job is pretty easy. My control likes IEnumerables.

         IEnumerable list = null;

         if (_datasource is IEnumerable)

         {

            list = (IEnumerable)_datasource;

         }

Wow, that’s great. Now we have an object called “list” which is my data source as an IEnumerable. But what if it’s an IListSource? Well, if it’s an IListSource then I want to convert it into an IEnumerable because I want to foreach through it. For those of you who aren’t fluent in C#, “foreach” is basically just short hand for calling IEnumerable::GetEnumerator() which returns an IEnumerator, then calling MoveNext() a bunch of times of that. Thus, you can only “foreach” through an object that implements IEnumerator.

So anyway, if I have an IListSource I have the following snippet of code:

         else if (_datasource is IListSource)

         {

            IListSource listsource = (IListSource)_datasource;

            IList memberList = listsource.GetList();

            list = (IEnumerable)memberList;

         }

Weird, what does that all do? So first, IListSource has a method called GetList(). This returns an IList. Know what’s truly amazing and comes in handy? The fact that IList is derived from IEnumerable. Using that, we can set our “list” object to an IEnumerable (which can enumerate an IListSource.)

So now we can iterate through, uhh – stuff. What stuff? I don’t know. The Array class’s GetEnumerator() implementation returns an IEnumerator that can iterate through objects in the array. Hashtable returns an IDictionaryEnumerator which represents key/value pairs. DataTable returns a collection of DataRowView objects. So I can’t just print those out to the screen. Plus, I’m not sure exactly what item I want. Wait, didn’t my “DataField” property specify what item to get? How does that fit in? How can I get a property of these objects based on a string? Well the answer to that is in Property Descriptors.

Property Descriptors are among the niftiest .NET freatures and allow you to abstract property names from the actual implementation of a class. I can say GetProperty and pass in a string, and my class knows what to return. Hashtable, DataRowView, etc all implement property descriptors which allow this to work. GetProperty on a DataRowView expects a name of a column, which is why you can set the DataField to a column name. Hashtable expects the string “Key” or “Value” which is why you can bind to a hashtable. One of these days, I’ll write a whole article on property descriptors and how can you write your own, but in the mean time we’re going to cheat and use a utility method that is part of the .NET framework. The DataBinder class has all sorts of methods that come in handy for people writing data bound controls. One of these methods is GetPropertyValue. You can pass in an object and which property you want to get, and even an optional formatting string, and it returns back a string for you. Using this, our foreach loop looks like this:

         foreach (object obj in list)

         {

            string text;

      if (_datafield != null)

               text = DataBinder.GetPropertyValue(obj, _datafield, String.Empty);

            else

               text = obj.ToString();

           

            Console.WriteLine(text);

         }

Well there you have it. You can now write your own data bound controls that work just like the ones that come with ASP.NET! Play around with this, see what sorts of data you can bind to your control and try writing your own collection classes as well. In my “Property Descriptors” article, I’ll be writing a collection class that you can bind to the ASP.NET DataGrid if you’re too cool for DataSets. Stay tuned!

Mike