ASP.Net Binding
ADO/Dataset notes - Asp2 quickstart
MSDN: GridView, DetailsVew, SQLDataSource, ObjectDataSource, Asp.net paging with ODS tutorial
Eval/Bind syntax
| <%# 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 %>' /> |
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].
- Set TypeName to the BLL name
- It's created using default constructor (to keep the instance for caching, override ObjectCreating -supply ObjectInstance- and ObjectDisposing - e.Cancel= true - events - see here)
- Populate the properties and call designated Select/Insert/Update/Delete methods (all made by non-cached reflection).
- For paging, EnablePaging="true" and SelectCountMethod plus (for nondefault named) MaximumRowsParameterName and StartRowIndexParameterName (NB the Configure DataSource wizard wrongly puts this in the asp:Parameter list)
- For InsertMethod there is an InsertParameters to map the properties to arguments. Otherwise set DataObjectTypeName.
- Optimistic concurrency using ConflictDetection= ConflictOptions.CompareAllValues
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)
- Example: DetailView/SqlDataSource with integer identity
- Example: SqlDataSource with guid primary key
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;
}
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);
}