guyS's WebLog

IShare, My DotNet Fingerprint

How to build a Re-usable, Generic Lookup Grid Picker

Version #1 – extended DataGrid web control, VS Designer does not supported

Guy Sofer
Applies to:

·                     C#

·                     Visual Studio .NET 2003 (1.1)

 

**source code + demo available

or ** download Version 1.0.1 (update on 15/5/2004), v1.0.1 changes list

 

Summary: The article’s purpose is to show how simple it is to take existing ASP.NET web control (DataGrid in this sample) and extend its functionality using inheritance in order to get a new web control with new specialties (Lookup Grid with a generic & rich functionality). It will cover topics like web control events & methods, JS functionality for client interactions and more info its worth knowing when building your own extended web controls.

In my previous article I explain how to implement a List2List composite web control. This article is my second web control I implemented – this means that the difficulty level is higher, because now I have more experienceJ.

I found that the Lookup Grid is essential in almost every rich web applications. That is why I decided to wrap the functionality of it as a web control that can be re-use over and over in different applications

We can use the Lookup Grid in entry forms when the end user should pick one or more values from an existing list of available items – Employee List, Location list, Customer list and the options are endless

 Lookup Grid web control feature list:

1.       Picker control that enable the user to display list of values in a tabular display

2.       Display different repository table’s data defined in a configuration Xml file

3.       Enable the user to pick from it single row value (radio button for each row) or multiple values (checkbox for each row)  – according the lookup configurations

4.       Enable the programmer to decide if he wants to display a pre-selected items when the lookup grid being called

5.       Return value/s as an Xml string which gives the developer more flexibility & power when parsing the Xml.

6.       Enable sorting & paging

Future wish List

1.       Data table from repository will be done by an object instance that will support a pre-define Lookup grid interface

2.       Data will retrieved by an object instance from different data sources provider

3.       Designer support – is a must

4.       Lookup grid configuration should enable to configure a Search Entry Form which the user will use to commit searches on the grid

5.       Enhance JS Functionality to process the return Xml value

6.       Cache the Lookup grid configuration Xml

7.       Rich Layouts support

Our current lookup grid version looks as follow:

 What this article will cover

·                     How to sample for building extended web control

·                     Web Controls events

·                     Adding JavaScript capabilities to our web controls

 

Solution

One of the requirements is to enable the Lookup grid to be generic by display different data from different DB tables. I saw an article few months ago that keeps the data retrieval definitions for a grid in Xml file. I adopted this great idea and I added an Xml configuration file that holds the data retrieval configurations. There I specified – how to connect to the DB (connection string), the fields we would like to display in the grid, the filters we would like to enable to end user (for future use) and the identity key for each Lookup grid row.

One of the Lookup Grid public properties is the XmlPath that holds these configurations

The Xml definitions for Employees Table from Northwind Database will look like this:

<lookupforms>

<emplookupform>

      <dbconnection encrypted="false"></dbconnection>

    <sql>select firstname,lastname,city, employeeid from employees</sql>

    <filters>

            <filter active="true">

            <datafield>firstname</datafield>

            <heading>First Name</heading>

            <type>string</type>

            <value>'a%'</value>

            </filter>

            <filter active="false">

            <datafield>lastname</datafield>

            <heading>Last Name</heading>

            <type>string</type>

            <value></value>

            </filter>

            <filter active="false">

            <datafield>employeeid</datafield>

            <heading>Employee ID</heading>

            <type>int</type>

            <value></value>

            </filter>

    </filters>

    <columns>

            <column>

                  <datafield>employeeid</datafield>

                  <heading>Employee ID</heading>

                  <dataformatstring>None</dataformatstring>

                  <sorting>false</sorting>

                  <sortexpression></sortexpression>

                  <visible>false</visible>

                  <returncolumn>true</returncolumn>

                  <type>int</type>

            </column>

            <column>

                  <datafield>lastname</datafield>

                  <heading>Last Name</heading>

                  <dataformatstring>None</dataformatstring>

                  <sorting>true</sorting>

                  <sortexpression>Asc</sortexpression>

                  <visible>true</visible>

                  <returncolumn>false</returncolumn>

                  <type>string</type>

            </column>

            <column>

                  <datafield>firstname</datafield>

                  <heading>First Name</heading>

                  <dataformatstring>None</dataformatstring>

                  <sorting>true</sorting>

                  <sortexpression>Asc</sortexpression>

                  <visible>true</visible>

                  <returncolumn>false</returncolumn>

                  <type>string</type>

            </column>

            <column>

                  <datafield>city</datafield>

                  <heading>City</heading>

                  <dataformatstring>None</dataformatstring>

                  <sorting>false</sorting>

                  <sortexpression></sortexpression>

                  <visible>true</visible>

                  <returncolumn>false</returncolumn>

                  <type>string</type>

            </column>

      </columns>

</emplookupform>

</lookupforms>

**The Filters are for future use and aimed to enable the user to search in the grid data rows

During the Lookup grid loading each column definitions in the Xml will be copy to a pre-define struct that will be part of a Columns arrays. This will make it easier to process during the grid binding

public struct GridColumn

      {

            public string           Name;

            public ColumnType Type;

            public string           DisplayName;

            public string           FormattingExpression;

            public bool             Visible;

            public bool             Sortable;

            public string           SortExpression;

            public bool             ReturnColumn;

      }

Private member – Grid Columns Array

GridColumn[] _GridColumns               = null;

In the Lookup Constructor we subscribed to the following DataGrid build in events:

Load event that occurred after the Page container complete its load – here we do our initializations,

ItemDataBound – occurred when one of the composite DataGrid row controls fire an event. I eventually did not use it. Usually we will need it in order to handle grid row level events (like button press even)

SortCommand - occur when ever the user press on one of the Headers

ItemDataBound – occur for each row in the Grid during the binding process. In this event we add a LinkButton control for each row (for single value select lookup grid) or a CheckBox control (for multiple lookup grid behavior). We also check when we fetch the Footer row. In this case we check if we should add a Save button control (for multiple select grid in a modeling window)

PreRander – fired just before the web control HTML is streaming to the client browser. This is good place to attach our client JS scripting.

PageIndexChange – fired when ever the user click on one of the grid pages

The full list is

this.Load+= new EventHandler(LookupGrid_Load);

this.ItemCommand+= new DataGridCommandEventHandler(dgLookup_OnItemCommand);

this.SortCommand+= new DataGridSortCommandEventHandler(dgLookup_OnSort); 

this.ItemDataBound+= new DataGridItemEventHandler(dgLookup_OnItemBound);

this.PreRender+= new EventHandler(dgLookup_OnPreRander);

this.PageIndexChanged += new DataGridPageChangedEventHandler(LookupGrid_PageIndexChanged);

           

Within the Load event handler (LookupGrid_Load) you can actually get the idea of the main flow of the Lookup Grid flow

Let’s have a look at the main flow and then explain it:

if (this.ReturnMultipleValues && this.UsingModelingWindow)

      this.ShowFooter = true;

                 

if (Page.IsPostBack && this.ReturnMultipleValues)

{

     //Construct the new Selected Items Array

      ConstructSelectedItems();

}

LoadXmlConfig();

CollectGridColumnsInfo(); //collect grid columns

ConstructGridColumns(); //create the grid columnx

DisplayGrid();

First I check if we should display the Grid Footer. We should display it when the Lookup is located in a Modeling Window container and only if it configured to enable Multiple Select Items.

On the Footer we located a Save Button that responsible to trigger the On Selected Event Handler where we returning the selected Items as Xml to the parent window JS function

Next, we check if the Grid loaded as part of postback event. If it does & the grid supported multiple selections we have to keep the selected items from the Request Object (where the checkbox selected items are) to a private Dictionary that we use to hold all the selected items per grid page.

The Key in the Dictionary is the Page and the value is a delimited string that holds the selected Items IDs

Here is the Dictionary declaration:

private HybridDictionary PagesSelectedItems

{

      get

      {

      if (ViewState["PagesSelectedItems"]==null)

            ViewState["PagesSelectedItems"] = new HybridDictionary();

 

      return (HybridDictionary)ViewState["PagesSelectedItems"];

}

      set

      {

      ViewState["PagesSelectedItems"] = value;

      }

}

                 

                 

The other method calls in the Load Handler deal with the loading of the Lookup configuration Xml and afterward displaying the Grid accordingly.

I find that it easier to use a local grid columns array instead of working directly with the Xml so I first, as you can see copy the Xml definitions into the local columns array.

ConstructGridColumns Method

In this method we construct the Grid Column as it was defined in the Xml

The column with the select row option I added during the ItemDataBound event handler as I specified.

DisplayGrid Method – does not do much. It’s responsible to get the Data for the display from the GetGridData Method (which return a DataSet) and activate the grid binding process.

 

The Lookup supported public properties are:

 

Property

Description

XmlLookupConfigPath

The Path of the Lookup/s Xml configuration file

LookupName

The Lookup Xml Section element where beneath it we can find all the lookup definitions

ReturnMultipleValues

Should the Lookup enable the user to select & return Multiple values or only a single value

UsingModelingWindow

Does the Lookup should return the selected values from a Modeling Window or by rasiing OnPostBack event with the return values when a Modeling window does not in use

ButtonSaveCSS

 

SortExpression

The current sort expression (support ascending/descending keyword)

UsingUpDownSortingImages

Indicate if we should use Images for Asc/Desc sorting options as part of the headers

SortAscImgPath

Ascending image path when enabled

SortDescImgPath

Descending image path when enabled

SQLQuery

The query we should use in order to get the data from the DB table. We initialize it using the Xml file sql element

OriginalSelectedValues

A list of input selected items values – that the user already selected – when given they will be display as selected in the grid 

ShowDefaultSelected

Show or Hide the default given selected items (OriginalSelectedValues)

GridCurrentPage

Current display grid page

 

 

Handling the Multiple Select checkboxes

During the ItemDataBound event handler we add in each row a regular HTML input element from type – checkbox. The checkbox NAME is the same for all the rows in the current grid page. Checkboxes input elements that using the same name in the HTML entry form will send to the server when a PostBack fired the list of selected items as string delimiter (comma delimiter).

This enables us to parse the selected items and process it when ever a postback occurred.  We do it in the ConstructSelectedItems method.

I also attach each onclick event at the checkbox element to a JS function handler (for the example purpose). The JS function called addRemoveItem and only show the selected checkbox in a JS alert window.

if (this.ReturnMultipleValues){

                             

oTdCheck.Text = "<input name='"+ this.ClientID +"_chkItem" + "'type='checkbox' onclick='addRemoveItem(this)'" +

" value='" + currentItemValue + "' " +

(this.IsItemSelected(currentItemValue)?"checked":"")+ "></input>";

 

e.Item.Cells.AddAt(0,oTdCheck);

}

 

Returning selected Item/s to the client JS

This is done within the btnSaveSingle_Command event handler (for single value select) and btnSaveMultiple_Command event handler for multiple select. The selected item/s been retrive using GetReturnValuesXml method. The retrieve method calls to GetGridData with input parameters which specified we interest only with the selected Items.

When working with HTML Modeling windows we can return values to the parent window using the following syntax –

top.returnValue = “a return value”;

From server side code we would stream the script to the client using Response.Write method

Page.Response.Write("<script language='javascript'>top.returnValue=unescape('" + strResults + "');window.close();</script>");

The parent window JS function (caller) looks as follow –

function ShowDialog(lookupform,ctrlid,allowMultiple){

var retval="";

var xmlResults = null;

                                                                               

retval=window.showModalDialog("DialogWinHelper.aspx?lookupform=" + lookupform + "&AllowMultiple="+allowMultiple,null);

 

                if(retval!="" && retval!=null)                {

                xmlResults = new ActiveXObject("Microsoft.XMLDOM");

                xmlResults.async = false;

                xmlResults.loadXML(retval);                                                                               

                document.getElementById(ctrlid).value=xmlResults.xml;

}//end of return Xml valid

}//end of function

As you can see the advantage of returning Xml string is valuable – you can easily parse the Xml using XMLDOM that is available on the client

How to use the Control

You should locate the web control in a dedicated aspx page.

Within this page you should specified the properties that reflect the Lookup display data & behaviour

Its should look like this

<shareControl:lookupgrid id="LookupGrid1" runat="server" CellPadding="1" CellSpacing="1" width="80%" ReturnMultipleValues="False"

UsingModelingWindow="True" XmlLookupConfigPath="LookupGrid.xml" LookupName="" AutoGenerateColumns="False">

      <ITEMSTYLE CssClass="cell3" HorizontalAlign="Left"></ITEMSTYLE>

      <HEADERSTYLE CssClass="ta_head3" HorizontalAlign="Left"></HEADERSTYLE>

      <ALTERNATINGITEMSTYLE CssClass="gridRequestAlternate"></ALTERNATINGITEMSTYLE>

      <COLUMNS>

            <ASP:BUTTONCOLUMN CommandName="select" Text="Select"></ASP:BUTTONCOLUMN>

            </COLUMNS>

</shareControl:lookupgrid>

Conclusions

The sample shows how easy it is to extend existing ASP.NET web controls. The Lookup grid can be enhance with the different wish list specified above and of course with other features I did not think about.

Feel free to use it & extend it (and I hope u will share it with us)

 

Comments

TrackBack said:

# April 8, 2004 9:59 PM

TrackBack said:

# April 30, 2004 4:50 PM

TrackBack said:

# April 30, 2004 4:55 PM

TrackBack said:

# May 10, 2004 1:47 AM

ashwin said:

can the same be build using VB.net if so can u help we with the code

# May 12, 2004 5:26 AM

Gary said:

Who wants to read an article that forces you to scroll sideways.

Obviously your screen resolution is much higher than mine.

Please try to keep us poor 'little screen' guys in mind when building a web page.
# May 13, 2004 11:24 PM

Guy S said:

Sorry Gary for that

Next time I will pay attention to this issue..



# May 14, 2004 1:10 AM

Code God said:

Who's gonna use this Piece of crap?
# May 14, 2004 10:43 AM

Code underling said:

Thanks for the article. Honeslty, I'm kinda perturbed that society still breeds people with the audacity to insult other people's creativity, time, and effort in teaching people. Code God, you get the official "I'm an asshole and the whole world knows it" award for the day
# May 14, 2004 12:05 PM

Guy S said:

Code God - u already send your opinion about it, and your opinion is clear enough - 10x!!.
No one is have to use it. If its so bad - let other learn from my mistakes.

I'm sure going to use this crap.
The publication (even worse as it is)
can teach few of the aspects of web controls coding,
JavaScript dialog window transfer data functionality and other topics..

I can agree there r things to improve and I hope I will in the future.
Meanwhile, this is it - and I will not be upset if u will skip my blog
and find the truth and code faith in other blogs.
# May 14, 2004 3:32 PM

TrackBack said:

# May 15, 2004 1:16 PM

Chad said:

Thanks for the great article. Was very helpful! Quick question, you mentioned that you adpated an idea from an article you saw a "few months ago that keeps the data retrieval definitions for a grid in Xml file." Can you give the source for that article as I'd be very interested in reading it.
# May 16, 2004 1:08 PM

Guy S said:

I wish I could - I tried to find in order to add it as additional reference for my extensions but could not find it.

What I saw in this article was:

Instead of using inheritance to extend the DataGrid control the author uses an
engine class component that take as a parameter a DataGrid instance and an XML
path that contains the grid definitions

Within the engine the Grid Header columns been constructed - using the Xml
definitions. In the ItemDataBound handler the Select link was added (I don't recall that there was an option
to select more then one element using checkboxes)

I will add the link to it - if I will find it.
# May 16, 2004 1:28 PM

Nik said:

Hi, Good work, I know this article is gonna help lot of people.

I think people like Code God don't deserve being in tech. field.

Regards,
Nik
# May 17, 2004 11:24 AM

Guy S said:

Thanks Nik,

I think Code God could be more constructive if he suggest a better alternative
or if he was point to thing that needs to improve
Then I could review my code and maybe rewrite it accordingly

Just saying - "hay, this is crap" - its not serious.
Or another feedback I got and reveal the infantile attitude of some developers out there was - Hay, I'm a better coder then u

I'm not in a contest with anyone!!! People, please grow up

constructive remarks can be something like -
U should use the DataSource property to keep the retrieved DataSet
or -
You should do more tests (which is true)
or
The download source does not compiled and so on

I also notice that developers r focus on the code itself

Its true that code and its quality is very important -
but I think an article can sometimes be worth only by suggesting a new and cool idea

Maybe LookupGrid its not new idea - but its definitely cool

I saw that a generic LookupGrid is necessary in lots of web applications.
I tried to implement it - and the results are quite good (IMHO)

If someone have a better solution I will be the first to use it -
Because I need such a solution for my applications
# May 17, 2004 12:26 PM

nandu said:

I downloaded the 1.0.1 code, but the web.config file seems to be missing in the zip file
# May 17, 2004 5:24 PM

Guy S. said:

I know the web.config is missing. I did not add it on purpose because I wanted to
avoid to add it by mistake with details that I don't want to expose.

Anyway - u should know I use OleDb to connect to the DB.
I also use web.config ConnStr key for the connection string value.
# May 17, 2004 11:54 PM

Nik said:

Hi Guy S,

Let me tell you what I learnt from your article.

Earlair I was facing problems in using ASPX pages in dialog boxes, when we use dialog boxes we can't post back (It opens up a new window).

But after going through your article I got an idea that we can use IFRAME control in a HTML page and load this HTML in the dialog box and can load our original ASPX page in this IFRAME. After this we can do as many post backs as we want.

Thanks and Regards,
Nik
# May 20, 2004 6:14 AM

Guy S. said:

This is good.

Regard IFrames holder pages when using Dialog windows -

One of my colleague developer suggested that instead of adding IFrames aspx files
helpers and increase the numbers of files in the project we will send a status flag to the ASPX that will be use as the dialog window and both as the Iframe holder.

In that ASPX we will use the Status flag (lets call it IFrame) as indicator to the page display behavious -

if IFrame!=null then the following code will be written to the client

if (Request.Params["IFrame"]!=null)
{
Response.Write ("<iframe src='thisFileNamePopUp.aspx' width=0></iframe>");
Response.End();
}

Otherwise - the Dialog will be open with the Popup display

Its also possible to put the above code in a base page that all the other PopUp
window pages can inherit from
# May 20, 2004 10:53 AM

Alex said:

There are no Web.config file in the source code :(
# May 21, 2004 6:33 AM

swaps said:

Can you please attach the web.config to this page? Also, there is no documentation as regards what each page does. Can you provide some documentation too...Thanx
# June 10, 2004 7:28 PM

Carlos Portugal said:

Hello,

I'm willing to build a control like yours, for a long time. Now that I've found this article, I can't donwload the control or the source. Can you review the links please!

Thank you. Keep doing great works like this :)

# June 4, 2007 5:08 PM

Jeba said:

Could you please share the code?

The download link is broken.

Thanks

# July 24, 2007 9:04 PM

niks said:

Nice article man!!!

# August 14, 2007 5:17 AM

konrad said:

Es la frase de valor  

http://eru1.myftp.biz/  

matais

# August 21, 2011 5:53 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)