Blog - Microsoft .NET, ASP.NET, AJAX and more

My Wrox Blox Books - Now on Amazon!

by Damien White 1/30/2009 10:00:23 AM

I just happen to be browsing Amazon today and noticed that my Wrox Blox books are available for purchase on Amazon.com.  Pretty cool stuff!  They were listed up there before, but weren't available directly through Amazon until now.  For a full description and the table of contents of each, just click on the Amazon links at the bottom of this post.  In addition, here are my previous announcements:

Both books are good guides for anyone using ASP.NET AJAX in either Visual Studio 2005 or 2008 using .NET 2.0 (with the AJAX Extensions) or .NET 3.5.

Shout it kick it on DotNetKicks.com Bookmark and Share

C# 3.0 - Auto-Implemented Read-Only Properties

by Damien White 8/22/2008 7:41:45 PM
Technorati Tags:

This isn't a new topic by far, but I still encounter the question of how to implement a read-only auto-implemented property, so here it goes. 

As I'm sure you are aware, C# 3.0 has a wonderful feature known as auto-implemented properties (sometimes referred to as Automatic Properties).  These clean up our code quite a bit when we have properties that are simply backed by a private member, and all we do it get and set that value. More...

Shout it kick it on DotNetKicks.com Bookmark and Share
Tags: , ,
Categories: .NET | C#
Actions: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

Wrox Blox - ASP.NET AJAX Control Development with Visual Studio 2008 and .NET 3.5 Framework

by Damien White 4/28/2008 11:28:17 PM
Technorati Tags: ,,

ASP.NET AJAX Control Development with Visual Studio 2008 and .NET 3.5 Framework Wrox BloxI'm happy to announce that my second Wrox Blox is done and available for purchase as of today!  As you may remember, back in January, my first Wrox Blox on the ASP.NET AJAX Control Toolkit was released.  Now, continuing with the ASP.NET AJAX theme, the new Wrox Blox deals with creating your own ASP.NET AJAX controls, from scratch.  As you may or may not know, control development is one of my favorite topics.  There are always times when you create something that you and others will useful in multiple applications, and these cases usually are good candidates for a server control.  This book takes the "Server Control" to the next level by enabling a rich client interface with ASP.NET AJAX.  If you are still working in VS 2005, don't let the title scare you away.  Even though the book focuses on VS 2008 and .NET 3.5, you'll still find the code and concepts apply within VS 2005 with .NET 2.0 and ASP.NET AJAX Extensions 1.0.  More...

Shout it kick it on DotNetKicks.com Bookmark and Share

Extending the GridView to Work With the New DataPager Control

by Dave Marini 3/19/2008 7:35:00 PM
Technorati Tags: ,


Recently I read an article by Scott Mitchell about the new ListView and DataPager controls that are included in the .NET Framework version 3.5. This got me curious as to how to adapt other well known databound controls like the GridView so that they can work together with the DataPager control.  After some light reading and rather heavy reflecting over the guts of the GridView control, I had a gameplan and was ready to go.  Extending a databound control to allow communication with the DataPager control isn't too difficult to do, but it does assume that you have an understanding of how the data is bound to the control. But why bother to do this when the GridView has its own paging control built in? Well, for one thing, being able to decouple the pager from the grid allows us to do some pretty cool things. One thing I can think of is having the GridView in the left bar and the paging control someplace in the main window. Also, we can have a grid with two pagers. Each keeps track of the current page consistently, regardless of which one we use to page through our data. Finally, the templating controls of the pager are very powerful compared to those of the GridView by itself.

Let's start with how a databound control can communicate with the DataPager. The DataPager control can be hooked up to any control that implements the IPageableItemContainer interface located in the System.Web.Extensions assembly. Here's a quick shot of what the interface looks like courtesy of .NET Reflector:

public interface IPageableItemContainer
{
    //Events 
    event EventHandler<PageEventArgs> TotalRowCountAvailable;

    // Methods  
    void SetPageProperties(int startRowIndex, int maximumRows, bool databind);

    // Properties 
    int MaximumRows { get; }
    int StartRowIndex { get; }
}

The MaximumRows and StartRowIndex properties of the interface are simply there to create a way for your databound control to determine the data window to display. This effectively defines the basis for what you will consider to be a "Page" of data.  The SetPageProperties method is important in the interaction with the DataPager because this is the method in your control that the DataPager will call whenever you click on any of the DataPagerField controls (The Next or Previous Buttons, for example).  Finally, the interface defines an event called TotalRowCountAvailable.  This event tells the DataPager how many records are in the data being bound to your control. This is obviously important as the DataPager uses the total row count to determine how man page buttons to render or whether to disable the next or previous buttons so that you don't go to a nonexistent page index.

So let's begin extending the GridView with the hooks for the DataPager.  Thinking about the IPageableItemContainer interface in terms of the GridView, we realize that MaximumRows is equivalent to the existing PageSize property, and StartRowIndex can be calculated from the existing PageSize and PageIndex properties.  We also prepare the event by declaring it and creating a corresponding event invoker. Since I want my new Paging capability to be the default for this grid, I force the Pager to hide itself every page load. You can add a toggle to this behavior should you so desire. Finally, we stub out the SetPageProperties method, but we'll leave it blank for now as we'll revisit it shortly. So far our new GridView looks like this:

public class PageableGridView : GridView, IPageableItemContainer 
{ 
     public PageableGridView() : base() 
     { 
          PagerSettings.Visible = false; 
     } 

     public event EventHandler<PageEventArgs> TotalRowCountAvailable; 

     public int MaximumRows 
     { 
          get{ return this.PageSize; } 
     } 

     public int StartRowIndex 
     { 
          get{ return (this.PageSize * this.PageIndex); } 
     } 

     protected virtual void OnTotalRowCountAvailable(PageEventArgs e) 
     { 
          if (TotalRowCountAvailable != null) 
                TotalRowCountAvailable(this, e); 
     }

     protected virtual void SetPageProperties(int startRowIndex, int maximumRows, bool dataBind) { } 
} 

The good news is that we're more than half way there. Now is where things get a bit more complex.  We still need a way to set the control with the pagesize and starting row values based on what the DataPager says we need to display once the user clicks on one of its buttons.  This is where the SetPageProperties method comes into play. Here is a basic implementation that gets the job done:

protected virtual void SetPageProperties(int startRowIndex, int maximumRows, bool dataBind)
{
if (databind)
{   
             PageSize = maximumRows;

             int newPageIndex = (startRowIndex / PageSize);

             if (PageIndex != newPageIndex)
             {
                 OnPageIndexChanging(new GridViewPageEventArgs(newPageIndex));

                 PageIndex = newPageIndex;

                 OnPageIndexChanged(EventArgs.Empty);
             }
}

        RequiresDataBinding = databind;
} 

When the DataPager sends the grid its paging information, the grid needs to set any appropriate params on itself to prepare to bind to the right window of data. The 2 properties that the grid is already equipped to do this with are the PageSize and PageIndex properties. These properties can be calculated from the information sent into the method, so we set them.  Of course, if the Page is being changed, we should probably fire the OnPageIndexChanging event. This means if you aren't binding to a datasource, you'll still need to make sure to handle this event. Finally, we instruct the grid to rebind itself should the DataPager be in the process of binding to data.  This is a basic implementation so you'll want to do any data integrity checks here as well, for example checking to ensure that the new PageIndex and StartRowIndex values are in the valid range.

There's only one thing left to do to complete our integration of the GridView to the DataPager. For the DataPager to render the appropriate number of page buttons, or for it to know when to disable the next or previous page buttons on the pager, it needs to know how many total rows there are. This, coupled with the Page Size, which is specified declaratively on the DataPager itself, help it determine the number of pages that the data source contains. The problem is, this information isn't known to the pager. It is known to the GridView however, and we need to get it from there and pass it on to the DataPager. But when can we be sure that the GridView has this data?  The GridView control inherits from CompositeDataboundControl. This type contains a special variant of the CreateChildControls method that also takes in a property indicating whether the control is being bound to data or simply re-rendered.  A little reflecting shows that the GridView uses this method to bind to its data source. Knowing this, it appears this is the place we will want to inject our trigger for the TotalRowCountAvailable event to be raised. This gets a little complicated because we need to handle the case where we're binding to a datasource manually or using a DataSource Control like ObjectDataSource or SqlDataSource, which have the total row count specified within them. A couple of helper methods are required to ensure we get the right value:

//Gets row count from SqlDataSource and the like... 
private int _GetTotalRowsFromDataSourceObject(IEnumerable dataSource)
{
    DataSourceView view = this.GetData(); if (AllowPaging && view.CanPage && view.CanRetrieveTotalRowCount)
        return base.SelectArguments.TotalRowCount;
    else
        return (PageIndex * PageSize) + _GetSourceCount(dataSource);
}

//Gets the row count from a manually bound source or from a source in viewstate 
private int _GetSourceCount(IEnumerable dataSource)
{
    ICollection source = dataSource as ICollection;

    return source != null ?
         source.Count :
         (from x in dataSource.OfType<object>() select 1).Sum();
}

The _GetTotalRowsFromDataSourceObject method retrieves the total number of records from the DataSource object, if it's available. This depends on a couple of things, such as whether the EnablePaging property was set on the DataSource Control and whether the Object has completed the query operation. Worst case, we return a single page of data and be done with it.  The _GetSourceCount method is used for two particular occasions. First, it's how you get the row count in the event that you bind the grid's DataSource property manually and then call DataBind(). Secondly, this method will also prove useful when the grid is rebinding after a postback to data that is stored in the viewstate. In both cases, we use a little linq to extract the total number of resulting data items (or rows in the case of viewstate) in the datasource. Now let's see how we use these methods to tie it all together:

protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
   {
       int baseResult = base.CreateChildControls(dataSource, dataBinding);

       if (dataSource != null)
       {
           int dataSourceCount = (IsBoundUsingDataSourceID && dataBinding) ?
               _GetTotalRowsFromDataSource(dataSource) :
               _GetSourceCount(dataSource);

           OnTotalRowCountAvailable(new PageEventArgs(StartRowIndex, MaximumRows, dataSourceCount));
       }

       return baseResult;
   }

The base control's CreateChildControls method is called first, since the grid uses it to actually do the databinding.  if there isn't any data to bind to, there will not be a reason to notify the pager, so we check to make sure we have data, then determine the number of rows in the source through the above mentioned process.  Finally, we fire off the event with the derived data and then we return the original result so as not to affect any other operations that may rely on it.  We now have a GridView that can be coupled with the new DataPager control.  Here's a sample of markup that we would use to use these controls together on a page:

<asp:DataPager ID="DataPager1" runat="server" PageSize="2" PagedControlID="grid2">
    <Fields>
        <asp:NextPreviousPagerField />
    </Fields>
</asp:DataPager>
<custom:pageablegridview id="grid2" runat="server" autogeneratecolumns="true" allowpaging="true"
        onpageindexchanging="grid2_PageIndexChanging" />

Notice that I don't have to specify the PageSize property of the GridView because the PageSize property on the DataPager will be in control of this value. To ensure this, we could shadow the PageSize property on the grid, but this will suffice for now.  So that's really all there is to it.  For newly created server controls that are bound to data, making these inroads to allow your control to work with the DataPager is pretty simple. For the existing controls, like the GridView, it's just a matter of knowing how the binding takes place and then injecting the functionality someplace where you know the needed values will exist.

Shout it kick it on DotNetKicks.com Bookmark and Share