ASP Detail View
DetailsView is nice but there's a lot of similar code every time. Like this... (In practice, all the BoundFields would be TemplateFields because you need validators)
This uses an identity column. See a Guid version.
Markup
<%-- SqlDataSource --%>
<asp:SqlDataSource ID="SqlDataSourceDetail" runat="server" ConnectionString="<%$ ConnectionStrings:ConnectionString %>"
InsertCommand="INSERT INTO [Product] ([Name], [Price], [AvailableFrom], [Category_Id]) VALUES (@Name, @Price, @AvailableFrom, @Category_Id);SET @Id = SCOPE_IDENTITY()"
SelectCommand="SELECT [Id], [Name], [Price], [AvailableFrom], [Category_Id] FROM [Product] WHERE ([Id] = @Id)"
UpdateCommand="UPDATE [Product] SET [Name] = @Name, [Price] = @Price, [AvailableFrom] = @AvailableFrom, [Category_Id] = @Category_Id WHERE [Id] = @Id"
OnInserted="SqlDataSourceDetail_Inserted">
<SelectParameters>
<asp:QueryStringParameter DefaultValue="0" Name="Id" QueryStringField="Id" Type="Int32" />
</SelectParameters>
<UpdateParameters>
<asp:Parameter Name="Name" Type="String" />
<asp:Parameter Name="Price" Type="Decimal" />
<asp:Parameter Name="AvailableFrom" Type="DateTime" />
<asp:Parameter Name="Category_Id" Type="Int32" />
<asp:Parameter Name="Id" Type="Int32" />
</UpdateParameters>
<InsertParameters>
<asp:Parameter Name="Name" Type="String" />
<asp:Parameter Name="Price" Type="Decimal" />
<asp:Parameter Name="AvailableFrom" Type="DateTime" />
<asp:Parameter Name="Category_Id" Type="Int32" />
<asp:Parameter Name="Id" Type="Int32" Direction="Output" />
</InsertParameters>
</asp:SqlDataSource>
<%-- Foreign key datasource --%>
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:ConnectionString %>"
SelectCommand="SELECT [Id], [Name] FROM [Category]"></asp:SqlDataSource>
<%-- UI- DetailsView --%>
<asp:DetailsView runat="server" ID="DetailsView1" DataSourceID="SqlDataSourceDetail"
DataKeyNames="Id" AutoGenerateEditButton="True" AutoGenerateInsertButton="True"
AutoGenerateRows="False" OnItemInserted="DetailsView1_ItemInserted" OnItemUpdated="DetailsView1_ItemUpdated"
OnModeChanging="DetailsView1_ModeChanging">
<Fields>
<asp:BoundField DataField="Id" HeaderText="Id" InsertVisible="False" ReadOnly="True"
SortExpression="Id" />
<asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" />
<asp:BoundField DataField="Price" DataFormatString="{0:c}" HeaderText="Price" SortExpression="Price" />
<asp:BoundField ApplyFormatInEditMode="True" DataField="AvailableFrom" DataFormatString="{0:d}"
HeaderText="AvailableFrom" SortExpression="AvailableFrom" />
<asp:TemplateField HeaderText="Category">
<EditItemTemplate>
<asp:DropDownList runat="server" ID="ddlCategory" DataSourceID="SqlDataSource1" DataValueField="Id"
DataTextField="Name" SelectedValue='<%# Bind("Category_Id") %>' />
</EditItemTemplate>
</asp:TemplateField>
</Fields>
</asp:DetailsView>
Code Behind
#region Standard DetailsView Events
/// <summary>
/// Initializes the DetailsView (called from Page_Load)
/// Checks querystring id to swap the detailsView mode between insert and update
/// </summary>
/// <returns></returns>
private bool DetailsViewInit()
{
if (string.IsNullOrEmpty(Request.QueryString["Id"]))
DetailsView1.ChangeMode(DetailsViewMode.Insert);
else
{
int result;
if (!Int32.TryParse(Request.QueryString["Id"], out result))
{
Page.Validators.Add(new BusinessValidationError("The ID is not in correct format."));
DetailsView1.Visible = false;
return false;
}
DetailsView1.ChangeMode(DetailsViewMode.Edit);
}
return true;
}
/// <summary>
/// After you've updated or inserted or cancelled, this is what happens next...
/// </summary>
protected void PostUpdateAction()
{
Response.Redirect("TestGridView.aspx");
//or hide/show panels
}
//if you're editing and Select returns no data, handle it
protected static void DetailsView1_ItemCreated(object sender, EventArgs e)
{
var dv = (DetailsView)sender;
if ((dv.CurrentMode == DetailsViewMode.Edit) &&
(dv.DataItemCount == 0))
{
dv.Page.Validators.Add(new BusinessValidationError("ID doesn't exist"));
return;
}
}
protected void DetailsView1_ModeChanging(object sender, DetailsViewModeEventArgs e)
{
if (e.CancelingEdit) PostUpdateAction();
}
protected void DetailsView1_ItemUpdated(object sender, DetailsViewUpdatedEventArgs e)
{
if (HasSqlError(e)) return;
PostUpdateAction();
}
protected void DetailsView1_ItemInserted(object sender, DetailsViewInsertedEventArgs e)
{
if (HasSqlError(e)) return;
PostUpdateAction();
}
#endregion
/// <summary>
/// Handles the Inserted event of the SqlDataSourceDetail control.
/// Shows the identity that has been created
/// (because we added SET @Id = SCOPE_IDENTITY() tp the commandText)
/// </summary>
protected void SqlDataSourceDetail_Inserted(object sender, SqlDataSourceStatusEventArgs e)
{
if (e.Exception == null)
{
DbCommand command = e.Command;
int id = (int)command.Parameters["@Id"].Value;
Page.Validators.Add(new BusinessValidationError(id + " added"));
}
}
#region HasSqlError overloads
/// <summary>
/// Determines whether has SQL error. Called from DetailsView1_ItemUpdated
/// </summary>
protected bool HasSqlError(DetailsViewUpdatedEventArgs e)
{
if (!HasSqlError(e.Exception)) return false;
e.ExceptionHandled = true;
return true;
}
/// <summary>
/// Determines whether has SQL error. Called from DetailsView1_ItemInserted
/// </summary>
protected bool HasSqlError(DetailsViewInsertedEventArgs e)
{
if (!HasSqlError(e.Exception)) return false;
e.ExceptionHandled = true;
return true;
}
/// <summary>
/// Determines whether has SQL error, the foundation.
/// You probably should use template fields with validators, but you probably won't check uniqueness constraints.
/// </summary>
protected bool HasSqlError(Exception ex)
{
if (ex != null)
{
string msg = ex.Message;
if (ex is FormatException) msg = "Invalid data- check numbers and dates";
SqlException exSql = ex as SqlException;
if (exSql != null)
{
switch (exSql.Number) //check for others if rqd
{
case 2627:
msg = "Data is not unique";
break;
case 515:
msg = "Mandatory fields must be entered";
break;
}
}
Page.Validators.Add(new BusinessValidationError(msg));
return true;
}
return false;
}
#endregion