Cool DataGrid Linking Tricks

With a few tips under your belt, DataGrid links are a snap.

asp.netNOW Q&A

 

LANGUAGES: C#

TECHNOLOGIES: DataGrids

 

Cool DataGrid Linking Tricks

With a few tips under your belt, DataGrid links are a snap.

 

By Josef Finsel

 

I am building a photo gallery that accesses the locations of JPEGs and GIFs from an Access database. I have a thumbnails page that displays all the thumbnails, and I want to be able to click on a thumbnail to display the full-sized image, then move forward and backward through the full-sized images.

 

Can I bring up a DataGrid that is set to page one record at a time and - using the value in Request.Querystring - fast forward to the requested record (from a value in a Request.Querystring?

- DM, Australia

 

You can do this, but it involves two tricks. First, you must know which page you need to be on when you open the grid. I've got a simple demo consisting of two pages. The first page has a page-enabled DataGrid that uses alphanumeric characters. When you click on a character, you're sent to a page that contains the same data in  one-row-per-page format; you're sent to the character you specified on that page.

 

Take a look at the AlphabetGrid page. It starts out with a standard DataGrid with paging turned on and code that loads a two-column DataView you can bind to the grid:

 

private DataView CreateDataSource()

   {

      DataTable dt = new DataTable();

      DataRow dr;

      dt.Columns.Add(new DataColumn("Letter",

       System.Type.GetType("System.String")));

      dt.Columns.Add(new DataColumn("LetterValue",  

       System.Type.GetType("System.Int16")));

      for (int i=65; i<=122;i++)

         {

           dr = dt.NewRow();

           dr[0] = Convert.ToChar(i) ;

           dr[1] = i - 65;

           dt.Rows.Add(dr);

         }

      DataView dv= new DataView(dt);

      return dv;

}

 

Now that you can see the data, take a look at the grid code. Simply drag the DataGrid onto your page, select it, and click on the Property Builder link at the bottom of the Properties page (see Figure 1). One important thing to note about the Property Builder is the checkbox at the top of the page that says "Create columns automatically at run time." If this box is checked, the DataView's two columns will be added when binding the data, even though you've bound one of them already.

 


Figure 1. The Property Builder makes setting up columns for a DataGrid a snap.

 

Now you can set up the hyperlink. In the Property Builder, take a quick look at the "HyperLinkColumn properties" section, which shows you both what is common to all the Property Builder boxes as well as what's unique to the hyperlink. Each column has a box for the Header text, Footer text, and Header image. Of more importance to hyperlinks are the boxes in the bottom half of the Property Builder, which display the properties for a hyperlink's three basic elements: the link itself, the text that displays where the link is going, and the target of the link. These three elements are configurable. If you put data in this section's "Text" or "URL" boxes, the information will display as static data in the DataGrid.

 

Let's take a look at how to use dynamic information, instead. You can select a column of information from the data you bind to the grid to show up in the text of the link, as a part of the link itself, or both. Because you will show a list of characters in your grid, select the Letter column in the "Text field" box and the LetterValue column in the "URL field" box. The only trick is to format the URL string to provide a QueryString. In the hyperlink, you can define a single URL value and reference it using {0}. When the page is rendered, the {0} is replaced by the value in the column defined by URL Column. All of this creates a DataGrid that looks like this:

 

  AllowPaging="True"

  OnPageIndexChanged="dgLetter_PageIndexChanged"

  AutoGenerateColumns="False">

   

     

       DataNavigateUrlField="LetterValue"

       DataNavigateUrlFormatString=

        "Letter.aspx?PageNo={0}" DataTextField="Letter"

       HeaderText="Letter">

   

 

Now that we've taken care of the basics of the initial form, let's get to the question at hand: How do you set a single-row DataGrid in the middle of the data set, then page back and forth through it properly? In this first demo, the page number is passed along in the form of the LetterValue. This means the Page_Load event needs to set our spot in the DataSet:

 

private void Page_Load(object sender, System.EventArgs e)

{

   if (Request.QueryString.Count > 0 && ! (Page.IsPostBack))

   {

      int iPage;

      string sPage;

      sPage = Convert.ToString(Request.QueryString["PageNo"]);

      if(sPage != "" && IsNumeric(sPage))

         {

            iPage = Convert.ToInt16( sPage);

            dgLetter.CurrentPageIndex = iPage;

         }

      }

      BindGrid();

}

 

Note that IsNumeric is not a built-in C# function, but it is available in this article's downloadable code. This code snippet performs two critical checks. The first is whether there are any Named/Value pairs in the QueryString. The second is whether or not the page has posted back to itself. These checks are important because the navigation links post back to the page and don't change the URL that shows up in the address box. If you don't check IsPostBack, your navigation won't work because it will keep forcing the page to the requested page on the Page_Load event.

 

It's also important to set the CurrentPageIndex before you bind the data. Once you've bound the data, it doesn't change correctly. In fact, if you bind the data and then set the CurrentPageIndex, you'll find that your page appears to be one click behind in the navigation - a frustrating experience.

 

Now that I've covered how to do this when you know the page number, let's look at how to do it when you get passed something you need to look up. You'll need to modify the Page_Load event slightly and separate the binding so you can access the DataView first. Once you've got the DataView, we can search it by setting the Sort column and issuing a Find command. This returns the row value, which we can use as the page value. To simplify this search and differentiate between a lowercase q and capital Q correctly, convert the character value to an integer and search on it. The page navigation works the same:

 

private void Page_Load(object sender, System.EventArgs e)

{

   DataView dv = CreateDataSource();

   if (Request.QueryString.Count > 0 && ! (Page.IsPostBack))

   {

      string sLetter = Request.QueryString["Letter"];

      if(sLetter != "" )

      {

          dv.Sort="LetterValue";

         int iPage =dv.Find(Convert.ToInt16(sLetter[0]));

         dgLetter.CurrentPageIndex = iPage;

      }

   }

   dgLetter.DataSource = dv;

   dgLetter.DataBind();

}

 

I have a DataGrid that utilizes template columns. I need to set up the links dynamically based on the item. I designed a class that contains the data I need and populated an ArrayList for the data source.

 

My problem is I need to use more than one field in the DataNavigateUrlField. I am fine when I use a HyperLinkColumn, but I need to add more than one field contained in the data source to the QueryString. When I add an additional DataNavigateUrlField, it throws an error. I tried using a comma-separated list in the DataNavigateUrlField, but it doesn't like that either.

- EB from Columbus, Ohio

 

Although everything might not be possible in ASP.NET, this not only is possible but it's fairly simple. The trick is to ignore most of what I said in the answer to the previous question. The hyperlink column is handy, but it's limited only to one variable piece of information. To have multiple columns, you need to switch from a column to a template column. A template column allows you to customize the look and feel of the column by using what appears to be inline coding.

 

Let's start by going back to the Property Builder and selecting the "Convert this column into a template column" link at the bottom of the Property Builder page to create this code:

 

   

      

       Text='<%# DataBinder.Eval(Container, "DataItem.Letter") %>'

      NavigateUrl='<%# DataBinder.Eval(Container, "DataItem.LetterValue", "Letter.aspx?PageNo={0}")

      %>'>

      

   

 

The key difference here is how the DataBinder is used. The DataBinder is the class that manages connections between controls and data. The DataGrid will automatically create these bindings, one for one, as it did in the hyperlink column created in the previous question. This is all that's happening here - a one-for-one use of the DataBinder for the hyperlink. And, because NavigateURL contains all the data you need, you simply can modify it. Let's first take a closer look at the DataBinder.Eval code.

 

The DataBinder.Eval code has three parameters. The first is the object you're evaluating the code into; in this case, use the standard Container object to refer back to your column. The second parameter is a reference to the column that contains the data you need. The third, and most important, is the format string that will format the output of the data. As you can see, the {0} is replaced by the value of the column in parameter 2. The DataBinder.Eval allows only one column to be referenced at a time, so you need to use multiple DataBinder.Eval statements strung together in the code. Here is one way to do this:

 

      NavigateUrl='Letter.aspx?PageNo=<%# DataBinder.Eval(Container, "DataItem.LetterValue")%>

            &Letter=<%# DataBinder.Eval(Container, "DataItem.Letter")%>'

 

There's nothing wrong with this method, performance-wise, but it does have some issues with reading and modifying the code. If someone wanted to remove the Letter column, they might remove the DataBinder.Eval statement and forget to remove the &Letter= section. It's much clearer and easier to maintain if you write it like this:

 

      NavigateUrl='DataBinder.Eval(Container, "DataItem.LetterValue", "Letter.aspx?PageNo={0}") + DataBinder.Eval(Container, "DataItem.Letter", "&Letter={0}")'

 

By using the format commands, identifying which values and columns go together is clearer, and the code is less likely to be altered incorrectly by having a column removed without removing the name that goes with it.

 

Next time I'll take a closer look at templates in DataGrids and Repeater controls. Until then, keep the questions coming and don't forget to check out the forums at http://www.aspnetpro.com/forums.

 

The files referenced in this article are available for download in VB .NET and C#.

 

Have a question? Send it to [email protected].

 

Josef Finsel is a software consultant with G.A. Sullivan, specializing in .NET and SQL Server. He has published a number of VB, .NET, and SQL Server-related articles, and, when he isn't hanging around the aspnetPRO forums, you can find him working on the syntax for FizzBin.NET - a programming language that works the way programmers have always suspected. He's also author of The Handbook for Reluctant Database Administrators (Apress).

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish