Follow @PDSAInc September 2011 - Posts - Paul Sheriff's Blog for the Real World

Paul Sheriff's Blog for the Real World

This blog is to share my tips and tricks garnered over 25+ years in the IT industry

Paul's Favorites

September 2011 - Posts

An Alternate Approach to a GridView – Part 2

After my last blog post on how to layout a different UI design instead of a normal tabular format, I had quite a few comments on how you would add sorting, paging and filtering and so on. That is one of the best parts about using the GridView control, you can still use all the normal sorting, paging and filtering that you would with a normal tabular GridView. If you take a look at Figure 1 you can see that I sorted the data using the SqlDataSource object, then applied paging and added a Search/Filtering mechanism. I also added a drop down list of field names so you can sort the grid using any field from the table you want. All of this was done and you will only have to add 1 line of code to perform the sorting!

Figure 1: Using sorting, paging and filtering on GridView

To make this form work, here is what I had to do.

I added a Label, TextBox and Button at the top of the page. I also added a DropDownList control and hard coded the collection of items with “CompanyName”, “LastName”, and “EmailAddress”.

The TextBox I named txtCompany and set its AutoPostBack property to true. The button is really just a placeholder and has no code behind it whatsoever. This allows me to tab off the TextBox which will cause a post back to occur, which in turn will force the SqlDataSource object that controls the grid to re-bind and apply the filter from the value typed into the TextBox.

To the GridView control, I added the following attributes:

  • AllowPaging="True"
  • PageSize="2"
  • AllowSorting="True"

To the SqlDataSource I then added modified the SelectCommand to add an ORDER BY on CompanyName to sort the data. I then added a FilterExpression with the expression “CompanyName LIKE ‘{0}’’. The {0} placeholder will be filled in with whatever value you type into the txtCompany text box.

Next I created a FilterParameters element within the SqlDataSource and created a ControlParameter element which specifies the name of the parameter, the control to use (txtCompany) and the property on the control that I wish to get the value for the parameter (Text).

<asp:SqlDataSource ID="custData"
   runat="server"
   ConnectionString="<%$
        ConnectionStrings:AdvWorksConnectionString %>"
    SelectCommand="SELECT * FROM SalesLT.Customer
                   ORDER BY CompanyName "
  FilterExpression="CompanyName LIKE '{0}%'">
  <FilterParameters>
    <asp:ControlParameter Name="CompanyName"
                          ControlID="txtCompany"
                          PropertyName="Text" />
  </FilterParameters>
</asp:SqlDataSource>

That is all there is to it! No code behind, just using normal GridView and SqlDataSource parameters.

Styling the Buttons

Another enhancement I added to this ASP.NET page is I styled the buttons a little so we did not have the default button look. This was easy to do by just adding a .CSS file and added the following class.

input.submit {
  color:#050;
  font: bold 84% 'trebuchet ms',helvetica,sans-serif;
  background-color:#fed;
  border:1px solid;
  border-color: #696 #363 #363 #696;
}

I added a <link> reference to the aspx page that referenced this style and set the CssClass=”submit” to each button control on the page.

Sorting the GridView

To sort the GridView control, you first set the AutoPostBack property to True on the DropDownList so when you change the value, a post back will occur and the SelectedIndexChanged event will fire. In this event you can then pass the selected value from the drop down list to the Sort method on the GridView.

protected void sortFields_SelectedIndexChanged(object sender,
  EventArgs e)
{
  grdCust.Sort(sortFields.SelectedValue.ToString(),
               SortDirection.Ascending);
}

Summary

In this blog you learned how to add paging, sorting and filtering to your grid view control. You also learned to apply a little styling to your buttons. I hope these two blog posts will help you to think about alternate UI designs other than the typical tabular format. I know your users will thank you. We have found that our users find user interfaces that are not so busy and show important data in a larger font size easier to use. Have fun!

NOTE: You can download this article and many samples like the one shown in this blog entry at my website. http://www.pdsa.com/downloads. Select “Tips and Tricks”, then “An Alternate Approach to a GridView-Part 2” from the drop down list.

Good Luck with your Coding,
Paul Sheriff

** SPECIAL OFFER FOR MY BLOG READERS **
We frequently offer a FREE gift for readers of my blog. Visit http://www.pdsa.com/Event/Blog for your FREE gift!

Posted: Sep 22 2011, 10:40 PM by psheriff | with no comments
Filed under: ,
An Alternate Approach to a GridView

I have long had a problem with using grids to display data to the user. I have an even bigger problem with editing on a grid. A grid is easy to implement for a developer, and this is normally why developers use them. However, a grid is normally not the best interface for a user. Now, note that there are always exceptions, but these should be the exception and not the rule. There are many reasons why a grid is not suitable for user consumption.

A grid presents too much data on the screen so the user’s eye has too much to try to concentrate on
A user will get much more tired at the end of the day using a grid as opposed to other display types
The user cannot distinguish between the data in each column since each column is very uniform and nothing stands out
The header for the data can scroll out of sight and thus the user can no longer distinguish what data is what in each column

The above is just some of the reasons why a grid should not be the appropriate choice for displaying a list of data to the user. Just take a look at Figure 1 and then take a look at Figure 2 and see which one you think is easier to read.

Figure 1: The normal GridView control

Figure 1: The normal GridView control

Figure 2: Using a Template will make your GridView easier to read for the user

Figure 2: Using a Template will make your GridView easier to read for the user

Now, both of the above .aspx pages use a GridView control. However, the version of the GridView shown in Figure 2 uses a TemplateField to layout each row of data in the GridView. The version shown in Figure 1 uses just the normal column controls in a GridView.

Each of the above GridView controls use a SqlDataSource object to retrieve data from the SalesLT.Customer table in the AdventureWorksLT sample database. This SqlDataSource object is shown in the code below:

<asp:SqlDataSource
       ID="custData"
       runat="server"
       ConnectionString="<%$
          ConnectionStrings:AdvWorksConnectionString %>"
       SelectCommand="SELECT * FROM SalesLT.Customer">
</asp:SqlDataSource>

The first GridView control uses a couple of ButtonField controls for the Edit and Delete hyperlinks shown in the first two columns, followed by BoundField controls to display each column of data. The first GridView source is shown in the code below but has all the formatting removed to keep the source simple.

<asp:GridView>
 <Columns>
  <asp:ButtonField CommandName="Edit" Text="Edit" />
  <asp:ButtonField CommandName="Delete" Text="Delete" />
  <asp:BoundField HeaderText="Company Name"
                   DataField="CompanyName" />
  <asp:BoundField HeaderText="First Name"
                   DataField="FirstName" />
  <asp:BoundField HeaderText="Last Name"
                   DataField="LastName" />
  <asp:BoundField HeaderText="Email Address"
                   DataField="EmailAddress" />
  <asp:BoundField HeaderText="Phone"
                   DataField="Phone" />
 </Columns>
</asp:GridView>

The second version of this GridView uses a TemplateField control within a single column in the GridView. In this TemplateField you can define any HTML you want. With just a little creativity you can create a much better layout than Figure 1.

The reasons the second version of the grid is better than the first are 1) The most important piece of data, the Company Name, is in a larger font than the other data that is less important; 2) You keep the labels close to the data as opposed to in headers on the grid that will eventually scroll out of sight; 3) Having the data go top left to bottom right in terms of importance is how most people in the western culture tend to process data.

The HTML code to create this version of the GridView is shown below. Again, all of the formatting code has been removed to keep the source code simple.

<asp:GridView ShowHeader="false">
 <Columns>
  <asp:TemplateField>
   <ItemTemplate>
    <div style="border: 1px solid gray; margin: 12px">
     <div style="font-size: x-large">
      <%# DataBinder.Eval(Container.DataItem,
                               "CompanyName") %>
     </div>
     <div>
      Contact:
      <%# DataBinder.Eval(Container.DataItem,
               "FirstName")%>&nbsp;
            <%# DataBinder.Eval(Container.DataItem,
                "LastName")%>&nbsp;&nbsp;
           (<%# DataBinder.Eval(Container.DataItem,
                "EmailAddress")%>)
     </div>
     <div>
      Phone:
      <%# DataBinder.Eval(Container.DataItem, "Phone")%>
     </div>
     <br />
     <div>
      <asp:Button ID="btnEdit" Text="Edit"
              CommandArgument='<%#
                DataBinder.Eval(Container.DataItem,
                                "CustomerID")%>'
            CommandName="Edit" runat="server" />
      <asp:Button ID="btnDelete" Text="Delete"
              CommandArgument='<%#
                DataBinder.Eval(Container.DataItem,
                                "CustomerID")%>'
              CommandName="Delete" runat="server" />
     </div>
    </div>
   </ItemTemplate>
  </asp:TemplateField>
 </Columns>
</asp:GridView>

As you can see, the second version will take a little more thought and a little more HTML code, but your users will thank you.

Summary

Next time you are designing how to display a lot of data to your users, really think about how you can make this display simpler. Normally all it takes is a little discussion with your user to find out what data is most important. That data should be made to stand out in your list. You can still use a GridView control, but take advantage of the TemplateField control and HTML to create a list that really works for your user. Remember, your user is the one that has to live with your application day in and day out. You want them thanking you at the end of the day, not cursing you!

NOTE: You can download this article and many samples like the one shown in this blog entry at my website. http://www.pdsa.com/downloads. Select “Tips and Tricks”, then “An Alternate Approach to a GridView” from the drop down list.

Good Luck with your Coding,
Paul Sheriff

** SPECIAL OFFER FOR MY BLOG READERS **
We frequently offer a FREE gift for readers of my blog. Visit http://www.pdsa.com/Event/Blog for your FREE gift!

Posted: Sep 20 2011, 01:37 PM by psheriff | with 4 comment(s)
Filed under: , ,
From Normal Collections to Generic Collections

Prior to .NET Version 2.0 if you wanted to hold a collection of string, integers, or objects you had to work with some of the classes in the System.Collections namespace.

This namespace contains classes and interfaces needed to define collections of objects. Some of the classes are listed in Table 1.

Class Description
ArrayList An ArrayList is similar to an array, but the ArrayList class allows you to add items without managing the size of the list yourself (much like a VB Collection object).
CollectionBase This class is the base for a strongly-typed collection, critical for creating collection classes, described below.
DictionaryBase This class is the base for a strongly-typed collection utilizing associated keys and values. This is similar to the Dictionary object found in VBScript and used by many ASP developers.
SortedList This class represents a collection of keys and values, and is automatically sorted by the key. You can still access the values with either the key or the index number.

Table 1. System.Collections classes

Assume you create a class called Customer. The Customer class has a number of properties, methods, and events. You then want to create several Customer objects within one structure so you can iterate through the collection, performing some operation on each object. You could just create a variable of type Array, ArrayList, or SortedList, but these standard collections can hold any type of object. In other words, you could add a Customer object as the first item in the collection, but you could then add an Employee object as the second item, a string as the third, and so on. If you try to iterate over this collection and apply the Update method, it might work on the Customer and Employee objects, but when your loop attempts to call the Update method of a String object, it will certainly fail.
To get around this sticky situation, you can create your own class that looks like a generic collection object, but restricts its members to only one data type. This new class will include the standard methods of the collection base type, such as the Add and Remove methods, as well as an Item property, but your class’ Add method will only allow you to add Customer objects.

The .NET Framework 2.0 introduced a new namespace called System.Collections.Generics which provides a series of classes that closely resemble the classes in System.Collections. However, these new classes automatically provide the ability to contain only one type of object.

System.Collections Example

Assume that you need to create a Customer object. The structure of the Customer object is very simple, containing just a CompanyName property. Imagine that you want to create a collection class named Customers (note the “s” on the end) to hold a set of Customer objects. You first need to create the Customer class as follows:

public class Customer
{
  public string CustomerName;
}

Next you create your new Customers collection class. Your class must inherit from the System.Collections.CollectionBase class. This base class gives you all of the functionality of a normal collection, but allows you to override any of the base methods, such as Add and Item. The following code provides the new class, and its overridden Add and Item members:

public class Customers : System.Collections.CollectionBase
{
 public void Add(Customer cust)
 {
  base.List.Add(cust);
 }

 public void Remove(int Index)
 {
  if(Index > Count - 1 || Index < 0) {
   // Return error message
  }
  else
   base.List.RemoveAt(Index);
 }

 // This is the "Item" indexer
 public Customer this[ int index ] 
 {
  get
  {
   if (index > Count - 1 || index < 0) {
    // Did not find Customer
    return null;
   }
   else
    return (Customer) base.List[index];
  }
 }
}

In your collection class, you’ll need to override the Add method so that instead of accepting a generic Object type, your method accepts only a Customer object. You can then use the internal List property from the base class to add the customer object to the internal collection of generic objects.

You also need to override the Item method so it returns a Customer object instead of a generic Object data type. This function converts the data in the built-in List property from a generic Object data type to a Customer data type. In C# you need to use the indexer syntax to return an item from this collection. This is done using the syntax shown below:

// This is the "Item" indexer
public Customer this[ int index ] 
{
 get
 {
  if (index > Count - 1 || index < 0)
  {
   // Did not find Customer
   return null;
  }
  else
   return (Customer) base.List[index];
 }
}

Using inheritance, you end up writing a lot less code to implement a collection class when compared to the code you had to write in older language that did not have inheritance. To use this class, you could write code like the following:

private void CustCollection()
{
 Customer cust;
 Customers custColl = new Customers();
 StringBuilder sb = new StringBuilder();

 cust = new Customer();
 cust.CustomerName = "Microsoft Corporation";
 custColl.Add(cust);

 cust = new Customer();
 cust.CustomerName = "PDSA, Inc.";
 custColl.Add(cust);

 sb.Append("Count = " + custColl.Count.ToString());
 sb.Append(Environment.NewLine);

 foreach(Customer c in custColl)
  sb.Append(c.CustomerName + Environment.NewLine);

 lblMsg.Content = sb.ToString();

 custColl.RemoveAt(0);

 lblMsg.Content += "After the removal = " + custColl.Count;
}

The above code creates two new Customer objects, sets the CustomerName property of each to a unique name and then adds each one to the Customers collection class. You can use the Count property from the base class to determine the number of items in the collection, even though you did not implement it in your Customers class. You can use the For Each iterator to loop through each customer object. You can also utilize the base class’ RemoveAt method to remove a specific item at a specified index in the collection.

System.Generics.List Example

Let's now take the same sample shown previously and see how this would work with Generics. The code is considerably simplified since the Generics List class handles all the normal add and remove operations. In addition, using the List<Customer> forces type-safety onto the collection. This means nothing but an object of the type Customers is allowed into this Generic List collection.

public class Customers2 : List<Customer>
{
}

You can now write the exact same code to manipulate this Customers2 class in the exact same way you use the Customers class.

private void CustomerCollection()
{
  Customer cust;
  Customers2 custColl = new Customers2();
  StringBuilder sb = new StringBuilder();

  cust = new Customer();
  cust.CustomerName = "Microsoft Corporation";
  custColl.Add(cust);

  cust = new Customer();
  cust.CustomerName = "PDSA, Inc.";
  custColl.Add(cust);

 sb.Append("Count = " + colcust.Count.ToString());
 sb.Append(Environment.NewLine);

 foreach(Customer c in custColl)
  sb.Append(c.CustomerName + Environment.NewLine);

 lblMsg.Text = sb.ToString();

 custColl.RemoveAt(0);

 lblMsg.Text += "After the removal = " + custColl.Count;
}

As you can see the code from a usage perspective is exactly the same, however, the Customers2 class has eliminated all of the code from the Customers class because of the use of the Generics List<T> class.

Summary

There really is no reason to use the CollectionBase class anymore. In fact, you can not use CollectionBase in Silverlight or Windows Phone apps. You may still find reasons to use ArrayList and possibly even SortedList or other non-generic collections, however, 99% of the time you will take advantage of the Generics namespace and classes.

NOTE: You can download this article and many samples like the one shown in this blog entry at my website. http://www.pdsa.com/downloads. Select “Tips and Tricks”, then “Generic Collections” from the drop down list.

Good Luck with your Coding,
Paul Sheriff

** SPECIAL OFFER FOR MY BLOG READERS **
We frequently offer a FREE gift for readers of my blog. Visit http://www.pdsa.com/Event/Blog for your FREE gift!

Posted: Sep 06 2011, 11:58 AM by psheriff | with 2 comment(s)
Filed under: ,
More Posts