static void

ASP Webforms Binding

ADO/Dataset notes
MSDN: GridView, DetailsVew, SQLDataSource, ObjectDataSource, Asp.net paging with ODS tutorial

Eval/Bind syntax

Note: do not confuse <%# binding with...

<%# Container.DataItem("Price") %> ASP1. Format: DataBinder.Eval(Container.DataItem, "Price", "{0:C}")
<%# Eval("Price", "{0:C}") %> ASP2. One way with optional formatting.
Eval == DataBinder.Eval (DataBinder==Container in ASP2)
<%# (((System.Data.DataRowView)Container.DataItem)["Price"]) %> Explcit cast, avoid reflection. To find the actual type (DataRowView here), add a <%# Container.DataItem %>
<%# Bind("Price") %> ASP2. Two way
<%# MyMethod(Container.DataItem) %> Remember this can be useful
<%# Container.DataItemIndex %> The row index. For instance, for a "manual" radio button in a gridView (Request["rPick"] will be the item index):

<input type="radio" name="rPick" id='r<%# Container.DataItemIndex %>' value='<%# Container.DataItemIndex %>' />

ASP Data access

ObjectDataSource

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" TypeName="PersonRecord"
       SelectMethod="FindAll"
       UpdateMethod="Update" DataObjectTypeName="PersonRecord" />
<asp:GridView ID="GridView1" runat="server" DataSourceID="ObjectDataSource1"
       AutoGenerateEditButton="true" />

The ASP ObjectDataSource is pretty limited for normal business objects/POCOs but it's okay for simple ActiveRecord type classes. In practice I use my ObjectAdaptor a lot, and sometimes you just have to downgrade to a dataTable (here's a base EntityDataSource that reflects the object to create a datatable [CodeBetter].

Obviously, there's no filtering unless you use a Dataset (/table/view) and paging and sorting has to be done manually (but fairly simple for a typed dataset).

Gridviews: Getting the Data/Row

From a asp:ButtonField int rowIndex = Convert.ToInt32(e.CommandArgument);
If you template it, you can do the same trick with one of the following:
CommandArgument='<%# ((GridViewRow) Container).RowIndex %>' or
CommandArgument='<%# DataBinder.Eval(Container, "RowIndex") %>' or
(won't work in nested UpdatePanel) CommandArgument='<%# Container.DataItemIndex %>'.

<asp:GridView ID="GridView1" runat="server" AutoGenerateEditButton="True"
    DataKeyNames="Id"
    onrowediting="GridView1_RowEditing" onrowcommand="GridView1_RowCommand" >
    <Columns>
        <asp:ButtonField CommandName="DeleteButton" HeaderText="Delete" Text="Delete" />
        <asp:TemplateField HeaderText="Delete (Template)">
            <ItemTemplate>
                <asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="false"
                    CommandName="DeleteTemplate" Text="Delete"
                    CommandArgument='<%# ((GridViewRow)Container).RowIndex %>' />
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
{
    GridView gv = (GridView)sender;
    //for ButtonField e.CommandArgument is the integer rowIndex
    //for templated columns it's not unless CommandArgument bound to ((GridViewRow)Container).RowIndex
    int rowIndex = Convert.ToInt32(e.CommandArgument);
    GridViewRow row = gv.Rows[rowIndex];
}
protected void GridView1_RowEditing(object sender, GridViewEditEventArgs e)
{
    GridView gv = (GridView)sender;
    int rowIndex = e.NewEditIndex; //AutoGenerateEditButton / CommandName="Edit"
    GridViewRow row = gv.Rows[rowIndex];
    int id = Convert.ToInt32(gv.DataKeys[rowIndex][0]);
}

GridView Events RowIndex/Row Data
RowCommand //if ButtonField or CommandArgument set as above
int rowIndex = Convert.ToInt32(commandArgument);
GridViewRow row = gv.Rows[rowIndex];
gv.DataKeys[rowIndex][0]
RowEditing int rowIndex = e.NewEditIndex;
GridViewRow row = gv.Rows[rowIndex];
RowUpdating int rowIndex = e.RowIndex; //if bound to DataSource
e.Keys[0];e.NewValues["Name"]

Gridviews and DataViews

Bound controls are pretty limited- the fact that DataFormatString will only work if HtmlEncode= false is particularly crap.

<asp:BoundField DataField="Date" HeaderText="Date"
  DataFormatString="{0:dd/MM/yyyy}" HtmlEncode="False" />
<asp:BoundField DataField="Cost" HeaderText="Cost"
  DataFormatString="{0:c}" HtmlEncode="False" />

Practically you have to template most columns (to add the validators). Oh yes, and the CompareValidator still doesn't understand currency symbols if Type="currency". Grrr.
ButtonFields with ButtonType="Image" always post twice. As a button, or a templated field, it's okay. (MS bug- marked "fixed" but it's still in 3.5)

If you're deleting, you'll get paging display bugs in GridView because ObjectDataSource doesn't return AffectedRows (SqlDataSource default paging works, but it loads all the data every time).

protected void DataSourceDeleted(object sender, ObjectDataSourceStatusEventArgs e)
{
    e.AffectedRows = (int)e.ReturnValue;
}

ImageField

Showing images in gridviews. DataImageUrlFormatString has the url, DataImageUrlField has the property. DataAlternateTextField (optionally with DataAlternateTextFormatString) can be used for alt text.

<asp:ImageField DataImageUrlField="CategoryId" DataAlternateTextField="CategoryName" DataImageUrlFormatString="~/Viewers/ImageViewer.ashx?id={0}"  />

GridView PagerRow

Adding text to the pager, without creating a template.

    //data is datasource; count is total number
    gv.RowCreated += delegate(object sender, GridViewRowEventArgs e)
                        {
                            if (e.Row.RowType == DataControlRowType.Pager)
                                AddPagerText(e.Row, count);
                        };
    gv.DataSource = data;
    gv.DataBind();
}
 
private static void AddPagerText(TableRow row, int count)
{
    Label lab = new Label();
    lab.CssClass = "PagerLabel"; //easy styling
    lab.Text = string.Format("{0} found. Result page: ", count);
 
    Table table = new Table(); //put into table like the page numbers
    TableRow tabrow = new TableRow();
    TableCell cell = new TableCell();
    cell.Controls.Add(lab);
    tabrow.Controls.Add(cell);
    table.Controls.Add(tabrow);
 
    //pager template render one cell containing an HTMLTable
    row.Cells[0].Controls.AddAt(0, table);
}

Dynamic Data

Dynamic Data includes scaffolding for Linq2Sql/EF data models (useful for simple Admin websites; it's customizable but I don't think anybody has used it for real-world websites). It also reads richer information about types (UIHints, foreign keys etc) for better presentation- this part is useful (see DataAnnotations.

There are VS2010 templates for EF and L2Sql. You can only have one model.

Use buddy classes to add metadata - add a partial class for the type with MetadataType attribute, and attribute the buddy class ([ScaffoldColumn(false)], UIHint, DisplayFormat, Editable, EnumDataType). You can also use the partial methods OnChanging/OnChanged - for validation, throw a DataAnnotations.ValidationError.

Structure

In existing websites

To use dynamic data behaviour (better validation etc) in existing standard gridView/DataSource pages. just add GridView1.EnableDynamicData(typeof(MyModel.Product)) in Page_Init.

You could also add a DynamicDataManager, and in Page_Init do instead DynamicDataManager1.EnableDynamicData( EntityDataSource1).

<asp:DynamicDataManager ID="DynamicDataManager1" runat="server" AutoLoadForeignKeys="true"  />
<asp:LinqDataSource ID="LinqDataSource1" runat="server" ContextTypeName="DataAccess.NorthwindDataContext"
    TableName="Products" EnableUpdate="true" EnableInsert="true" EnableDelete="true" >
</asp:LinqDataSource>
<asp:GridView ID="GridView1" runat="server" DataSourceID="LinqDataSource1"
    AutoGenerateEditButton="true" AutoGenerateDeleteButton="true">
</asp:GridView>

protected void Page_Init(object sender, EventArgs e)
{
    //GridView1.EnableDynamicData(typeof(Category));
    DynamicDataManager1.RegisterControl(GridView1);
}