Why Is Everything So Darn Declarative?

Introduction

It has been a great ride! Very few times in history have been more revolutionary than this era in which you live. Lots of great things occur at an astonishing speed (sometimes bad things). With the Internet, ideas flow more freely than ever before. As always, humans are learning how to exploit this new medium.

In the programming industry, there has always been this tendency to look for the holy grail of codeless software development. There are many ideas of having some sort of subject matter expert code (wizards) writing your tedious code for you. The danger you face as programmers is the tendency to grow dependent on these tools to the point where you lose your ability to code or perform certain tasks yourself. I personally don't have any problem with automation as long as it truly solves a problem for me without leaving me impotent. Often, you are given tools that are very glossy and cool but do not cover every possible situation. No tool can, really. So, when the tool fails, you are royally screwed. To me, that's a real problem.

Today, technology is coming at you at an astonishing rate! It is coming at a much faster rate than is humanly possible to assimilate. Already, getting your hand around something like the .NET Framework is nearly impossible for one individual to have full mastery of it. Microsoft tries really hard to make things easier for you by giving you things like IntelliSense, code completion, snippets, and wizards to help you be more productive. I believe that is a good thing. However, you should not forget your roots. You are programmers and, to some extent, more than that. You have a responsibility to yourself and your industry to maintain some level of competency. I believe you are moving too fast without any introspection, too quickly with little or no analysis of the situation. You become overly dependent on your tools to do the job. I hate to use a cliché but I would rather be taught how to grow my own food than be given a plate of food. Those of you who like science fiction and movies may remember the following line from a famous movie.

"Don't you see the danger in what you are trying to do here? You took what others had done and before you knew what you had, you packaged it and..."

I am, of course, paraphrasing from of a line in Jurassic Park. However, I believe it is very applicable to the point I am trying to make. My warning is for you to be aware that every time you take a shortcut in order to meet a deadline, it may come at a subtle but serious price. Be aware of the tradeoffs in question. I realize we all have to make compromises to get things done. But, be aware of the cost of such shortcuts to you as a developer. Okay, by this time you must be asking. What exactly is your point? I tell you by sharing a real life experience.

I am going to take a very small slice of a simple application I am currently working on. This application is not glamorous or sexy. I needed to have a dynamic page that could deal with multiple tables and somewhat dynamic column definitions. It would not have been practical to create a static page to handle each of the possible conditions. It had to be dynamic enough so that a rules change would not require a new maintenance page.

I set out to prototype my requirements a little at a time. I normally break down my problem into smaller problems, then try them out with code whose only purpose is testing the feasibility of my plan. The problem was to create a Master/Details page of a very dynamic nature. The traditional examples provided by Microsoft always involved the use of data source controls such as SqlDatasource and ObjDataSource. These guys and a dose of wizards and you had a static page that would do part of the job. And, the great thing was it was done in minutes! Only one problem, it was static. I could not see how I could turn this declarative crap into a dynamic page capable of handling multiple queries dynamically by one GridView and DetailsView control. I needed some code to do this.

I broke down my code even further and started with just a GridView on my page. I knew this control has a property called DataSource. I knew what it did based on my experience with DataGrid. Back in those days, you did a lot stuff programmatically. All I had to do was provide a reference to a DataSet or DataTable then invoke Databind method and I was done.

Dim b As New BusinessRules.BusinessRules
GridView1.DataSource = b.GetCustomerList
GridView1.DataBind()

I ran into a number of issues with the DetailsView, however. I decided to Google the problem. I spent a considerable amount of time on this and didn't find much. Then, I went to one of the Microsoft forums and asked the question. The response was surprising; some guy named Phil started to berate me about my improper use of the Detailsview and told me that what I wanted to do could not be done because was not designed to be used in that manner.

I don't know about you guys, but I felt those were fighting words. I proceeded to flame the guy to no end. A day later, I posted part of the answer on how to perform column definition and bind all programmatically, without the use of a datasource. This is what I intent to share with you guys in the hope that something of some value comes out of this. Maybe some of you have even a better approach. This I would love to hear, or just your opinion.

So, my prototype looked something like what you see below. I had a drop-down to simulate my dynamic table or queries and the idea was that this page would adapt to the query dynamically and render the information.

The finished product looked as shown above and it was all done programmatically.

Why Is Everything So Darn Declarative?

Designing the Project

You sould start with the grid. As I already stated, to bind the GridView all you need is the following:

Dim b As New BusinessRules.BusinessRules
GridView1.DataSource = b.GetCustomerList
GridView1.DataBind()

The first line is hitting an object that brings back a datatable. You would, of course, replace that with your own data access. The second line applies to the datasource property and, as usual, you call it databind. You probably seen several different incarnations of this, but that is basically it. Remember, in my exercise I am staying way from the declarative model. Everything had to be coded.

Obviously, now you need to see how to bind data to the DetailsView control.

Dim b As New BusinessRules.BusinessRules
DetailsView1.DataSource = _
   b.GetCustomerRow(ViewState("PrimaryKey").ToString)
DetailsView1.DataBind()

This is pretty much the same as the GridView. As far as the ViewState material goes, it is just how I associated the selected row of the grid with what I wanted displayed on the DetailsView. I will get to that in a moment. Basically, that is all you need to populate the controls manually.

I wanted to do everything manually, that meant Fields and all. To define the columns for the grid, the following was needed:

This first group represents the select button for the GridView control.

Dim cmdF As New CommandField
cmdF.EditText = "Select"
cmdF.ButtonType = ButtonType.Link
cmdF.ShowSelectButton = True
GridView1.Columns.Add(cmdF)

The rest represents the column definition.

' customerid
Dim bf As New BoundField
bf.DataField = "CustomerID"
bf.HeaderText = "Customer ID"
GridView1.Columns.Add(bf)

' CompanyName
bf = New BoundField
bf.DataField = "CompanyName"
bf.HeaderText = "Company"
GridView1.Columns.Add(bf)

' ContactName
bf = New BoundField
bf.DataField = "ContactName"
bf.HeaderText = "Contact"
GridView1.Columns.Add(bf)

' Phone
bf = New BoundField
bf.DataField = "Phone"
bf.HeaderText = "Phone"
GridView1.Columns.Add(bf)

This rest represents the key to use to retrieve a single row.

GridView1.DataKeyNames.SetValue("CustomerID", 0)

At this point, you have what you could easily have gotten if I left auto generate fields on (see the following figure).

[Fig2.png]

Expanding the command field and selecting the select command would do the same, except now everything would be declarative, not code. In the old days, this wizard would generate code that, in my opinion, was infinitely more instructive than the declarative code generated now.

Why Is Everything So Darn Declarative?

The preceding code defines the column you see on the Gridview below.

[Fig3.png]

Now, you're finished with the Gridview. It's time to move on to the DetailsView.

For the Detailsview, you specify the following columns:

' customerid
Dim bf As New BoundField
bf.DataField = "CustomerID"
bf.HeaderText = "Customer ID"
DetailsView1.Fields.Add(bf)

' CompanyName
bf = New BoundField
bf.DataField = "CompanyName"
bf.HeaderText = "Company"
DetailsView1.Fields.Add(bf)

' ContactName
bf = New BoundField
bf.DataField = "ContactName"
bf.HeaderText = "Contact"
DetailsView1.Fields.Add(bf)

' ContactTitle
bf = New BoundField
bf.DataField = "ContactTitle"
bf.HeaderText = "Title"
DetailsView1.Fields.Add(bf)

' Address
bf = New BoundField
bf.DataField = "Address"
bf.HeaderText = "Address"
DetailsView1.Fields.Add(bf)

' City
bf = New BoundField
bf.DataField = "City"
bf.HeaderText = "City"
DetailsView1.Fields.Add(bf)

' Region
bf = New BoundField
bf.DataField = "Region"
bf.HeaderText = "State / Province"
DetailsView1.Fields.Add(bf)

' PostalCode
bf = New BoundField
bf.DataField = "PostalCode"
bf.HeaderText = "Postal Code"
DetailsView1.Fields.Add(bf)

' Country
bf = New BoundField
bf.DataField = "Country"
bf.HeaderText = "Country"
DetailsView1.Fields.Add(bf)

' Phone
bf = New BoundField
bf.DataField = "Phone"
bf.HeaderText = "Phone"
DetailsView1.Fields.Add(bf)

' Fax
bf = New BoundField
bf.DataField = "Fax"
bf.HeaderText = "Fax"
DetailsView1.Fields.Add(bf)

Dim cmdF As New CommandField
cmdF.EditText = "Edit"
cmdF.ButtonType = ButtonType.Link
cmdF.ShowEditButton = True
cmdF.ShowInsertButton = True
cmdF.ShowDeleteButton = True
DetailsView1.Fields.Add(cmdF)

Columns are derived in the same manner as the GridView, with a few minor differences. In the grid, you use the columns collection and in the detailsview, you use the fields collection. The last code group defines the command field that allows you to link the grid and detailsview controls.

The following figure shows what the final product looks like.

[Fig4.png]

Why Is Everything So Darn Declarative?

That takes care of the data binding. A little more logic is needed for everything to work together. The meager examples I saw on the Internet relied heavily on declarative stuff something I did not want, given the dynamic nature of my page. I relied more on controls' events to glue everything together.

To link the grid to the view, I used the grid's RowCommand event as shown below. Note that I take advantage of view state to persist the key value:

Protected Sub GridView1_RowCommand(ByVal sender As Object, _
   ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs) _
   Handles GridView1.RowCommand
   If e.CommandName.Equals("Select") Then
      Dim RowIndex As Integer = _
         Convert.ToInt16(e.CommandArgument.ToString)
      ViewState("PrimaryKey") = _
         GridView1.DataKeys(RowIndex).Value.ToString
      ' this call will display the Detailsview as shown above.
      BindDetailsView()
   End If
End Sub

Because you're manually doing databinding, the DetailsView viewstate has to be turned off. I could have spent more time fixing this little bug, but instead I render the control every time I post back. This is something you probably should not do on production code.

To perform updates, you need to fake out the normal process. This particular event is normally used for validation and one last chance to fix or abort the update process. You can use it to manually update the database bypassing the integral update command that comes with the DetailsView.

Protected Sub DetailsView1_ItemUpdating(ByVal sender As Object, _
   ByVal e As System.Web.UI.WebControls.DetailsViewUpdateEventArgs) _
   Handles DetailsView1.ItemUpdating
   ' Dim new_customerid As String
   Dim new_CompanyName As TextBox = _
      CType(DetailsView1.Rows(1).Cells(1).Controls(0), TextBox)
   Dim new_contactName As TextBox = _
      CType(DetailsView1.Rows(2).Cells(1).Controls(0), TextBox)
   Dim new_ContactTitle As TextBox = _
      CType(DetailsView1.Rows(3).Cells(1).Controls(0), TextBox)
   Dim new_address As TextBox = _
      CType(DetailsView1.Rows(4).Cells(1).Controls(0), TextBox)
   Dim new_city As TextBox = _
      CType(DetailsView1.Rows(5).Cells(1).Controls(0), TextBox)
   Dim new_Region As TextBox = _
      CType(DetailsView1.Rows(6).Cells(1).Controls(0), TextBox)
   Dim new_PostalCode As TextBox = _
      CType(DetailsView1.Rows(7).Cells(1).Controls(0), TextBox)
   Dim new_Country As TextBox = _
      CType(DetailsView1.Rows(8).Cells(1).Controls(0), TextBox)
   Dim new_Phone As TextBox = _
      CType(DetailsView1.Rows(9).Cells(1).Controls(0), TextBox)
   Dim new_Fax As TextBox = _
      CType(DetailsView1.Rows(10).Cells(1).Controls(0), TextBox)

      Dim dt_old As DataTable = _
         CType(Cache.Get("Custdata"), DataTable)

      Dim dt_new As DataTable = dt_old.Copy

      dt_new.Rows(0).Item(1) = new_CompanyName.Text
      dt_new.Rows(0).Item(2) = new_contactName.Text
      dt_new.Rows(0).Item(3) = new_ContactTitle.Text
      dt_new.Rows(0).Item(4) = new_address.Text
      dt_new.Rows(0).Item(5) = new_city.Text
      dt_new.Rows(0).Item(6) = new_Region.Text
      dt_new.Rows(0).Item(7) = new_PostalCode.Text
      dt_new.Rows(0).Item(8) = new_Country.Text
      dt_new.Rows(0).Item(9) = new_Phone.Text
      dt_new.Rows(0).Item(10) = new_Fax.Text

      Dim b As New BusinessRules.BusinessRules
      b.UpdateCustomer(dt_old.Rows(0).Item(0), dt_new, dt_old)

      ' changing the mode here basically cancels the update wish
      ' that will normally follow this event.
      DetailsView1.ChangeMode(DetailsViewMode.ReadOnly)
      BindDetailsView()

Critical to extracting data from the DetailsView requires that long line that is cast into the appropriate control.

CType(DetailsView1.Rows(1).Cells(1).Controls(0), TextBox)

The BoundField default type contains labels for read only (displaying) and textboxes for inserting and updating. I copy the original table and populate it with the fields from the DetailsView. The business method required the new change as well as old data to perform concurrency checks. There probably are more ways of doing this than I can imagine. I am just showing one of many possible implementations. Finally, get the DetailsView control out of edit mode, refresh, and you're done.

You can look at the code for Insert and Delete on your own. The rest of my code just manages the dynamics of changing query. In this example, I only show the Customers and Products tables, but you could create some basic boiler plating for field definitions as shown and you're done.

Basically, that's it. I know that I have not covered everything in detail but I did not want to waste your time with the obvious. You can check that out on your own. Of course, I will make the source for this simple exercise available to those you who are interested.

I can understand why Microsoft pushes the declarative mode exclusively. One of the transitions from 1.1 to 2.0 was the new model for deployment driven by their belief that they could make the page more responsive. This dynamic model came at a price where partial classes were needed to hide the fact that the designer generated portion of code behind is now dynamic and no longer there which is why, I suspect, the declarative model is (at least in part) required. So, you can't cheat by peeking into the Designer-generated code to see how things are done. Now, you have to rely on reflection tools to reverse engineer all the stuff they are hiding. Microsoft does not want to stop and explain all this and instead pushes its declarative crap to the exclusion of all else. It is not necessary for them to coerce you into this way of doing things as the only solution. It should be your choice, not theirs. Since mid 2003, I've seen a shift from the open mind set back to a more heavy handed approach cloaked in a pretext—"see how easy we have made it for you!" Thanks, but no thanks!! DO NOT LIMIT MY CHOICES!!

Things Left to Do

How do you handle special situations like dropdown an maskedit controls? Once again, notice how weak the DetailsView control is from a UI perspective. The things I just motioned are non-trivial to do with the DetailsView even in declarative mode. It gets really interesting when you try to implement a template field programmatically.

In Part 2, I will show you how I came up with a solution for these issues.



About the Author

Angel Amador

Just an old developer

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Cisco and Intel have harnessed flash memory technology and truly innovative system software to blast through the boundaries of today's I/O-bound server/storage architectures. See how they are bringing real-time responsiveness to data-intensive applications—for unmatched business advantage. Sponsored by Cisco and Intel® Partnering in Innovation

  • Hybrid cloud platforms need to think in terms of sweet spots when it comes to application platform interface (API) integration. Cloud Velocity has taken a unique approach to tight integration with the API sweet spot; enough to support the agility of physical and virtual apps, including multi-tier environments and databases, while reducing capital and operating costs. Read this case study to learn how a global-level Fortune 1000 company was able to deploy an entire 6+ TB Oracle eCommerce stack in Amazon Web …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds