I am working on a C# webforms application. I have a SQL Server table named HkConv
, and it has the following structure:
Column Name | Data Type |
---|---|
ID | bigint |
Qust | nvarchar(max) |
Answ | nvarchar(max) |
I use 2 stored procedures that returns the following result sets:
SPHkConvSelect
to select table recordsSPHkConvDelete
to delete a record from the table
Requirements
- When the page loads, I want to display the records in the following format:
<div class="section">
{Delete Button Here}
<h2>[ID]: [Qust]</h2>
<div>
<h3>[Answ]</h3>
</div>
</div>
Here, the text in []
represents the column names from the table.
- The delete button should call a stored procedure to delete the corresponding record.
What I’ve tried
I am not sure how to structure the code in the webforms page to load the data into the div
elements dynamically and handle deletion.
Can someone provide the complete C# webforms code and Ajax to accomplish this?
SQL Server table and data example
Here’s the SQL script to create the table and insert sample data:
-- Create Table
CREATE TABLE HkConv
(
ID BIGINT IDENTITY(1,1),
Qust NVARCHAR(MAX),
Answ NVARCHAR(MAX),
Deleted INT DEFAULT 0
);
-- Insert Sample Data
INSERT INTO HkConv (ID, Qust, Answ, Deleted)
VALUES
(1, 'What is ASP.NET?', 'It is a web framework for building web apps.', 0),
(2, 'What is AJAX?', 'Asynchronous JavaScript and XML.', 0),
(3, 'What is C#?', 'A modern programming language by Microsoft.', 0);
Stored procedures
Get records:
CREATE PROCEDURE SPHkConvSelect
AS
BEGIN
SELECT ID, Qust, Answ, Deleted
FROM HkConv
WHERE Deleted = 0;
END
- Delete record:
CREATE PROCEDURE SPHkDelete
@ID BIGINT
AS
BEGIN
UPDATE HkConv
SET Deleted = 1
WHERE ID = @ID;
END
Here is my C# aspx.cs page code-behind:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
using System.Web.Configuration;
namespace Traffic
{
public partial class aidash : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
BindData();
}
}
private void BindData()
{
string connectionString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString; // Replace with your connection string name
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand("SPHkConvSelect", connection)) // Replace with your stored procedure name
{
command.CommandType = CommandType.StoredProcedure;
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
DataTable dt = new DataTable();
adapter.Fill(dt);
// Clear existing controls if any
pnlData.Controls.Clear();
foreach (DataRow row in dt.Rows)
{
Panel sectionDiv = new Panel();
sectionDiv.CssClass = "section";
Button deleteButton = new Button();
deleteButton.ID = "btnDelete_" + row["ID"].ToString();
deleteButton.Text = "Delete (" + row["ID"].ToString() +")" ;
deleteButton.CssClass = "deleteButton"; // Add CSS class for styling
deleteButton.CommandArgument = row["ID"].ToString(); // Store ID in CommandArgument
deleteButton.Click += DeleteButton_Click;
sectionDiv.Controls.Add(deleteButton);
Label h2Label = new Label();
h2Label.Text = $"<h2>{row["ID"]}: {row["Qust"]}</h2>";
sectionDiv.Controls.Add(h2Label);
Panel innerDiv = new Panel();
innerDiv.Controls.Add(new LiteralControl("<div>")); // Start inner div
Label h3Label = new Label();
h3Label.Text = $"<h3>{row["Answ"]}</h3>";
innerDiv.Controls.Add(h3Label);
innerDiv.Controls.Add(new LiteralControl("</div>")); // End inner div
sectionDiv.Controls.Add(innerDiv);
pnlData.Controls.Add(sectionDiv);
}
}
}
}
}
protected void DeleteButton_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
int idToDelete = int.Parse(btn.CommandArgument);
string connectionString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand("SPHkConvDelete", connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("@ID", idToDelete);
connection.Open();
command.ExecuteNonQuery();
connection.Close();
}
}
BindData();
}
}
}
This is the aspx
markup:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="aidash.aspx.cs" Inherits="Traffic.aidash" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title></title>
<style>
/* Facebook theme styling */
body {
font-family: Arial, sans-serif;
background-color: #f0f2f5;
color: #1c1e21;
margin: 0;
padding: 0;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<form id="form1" runat="server">
<asp:Panel ID="pnlData" runat="server"></asp:Panel>
</form>
</body>
</html>
The problem
When I click the delete button, all records disappear and nothing really gets deleted in the backend.
2
Answers
From the Examples of
Button.Click
Event, you should provide theEventHandler
instance to theClick
event.First up, WebForms has boatloads and boatloads of built in controls for this kind of task.
So no need to write all that code.
So, since we want "repeating" data, then of course you can use the "repeater" control. All that control does is "repeat" markup based on a data source.
Hence, say this markup:
Now, you are free to "tweak" the above markup, but the basic concept above is how you bind data into that markup.
Hence, the code behind to load up the data now becomes this:
And now the result is this:
As for the button click? Well, you can actually pick up any element within the given row click, but simply adding the PK ID to the command argument attribute of the button works well.
Hence, code to delete a row can be:
Of course, one becomes really tired of typing code over and over to pull data, or call some stored procedure, so I have a few global helpers in my general class of which I used in above, they are:
So note a few things:
We don’t have to write looping code, it simply not required.
We don’t have to write code to attach some event to the button. You have a standard button click event, and you can pass the given row PK value using the CommandArugment attribuite of the button.
And by using the repeater, then you write a minimal amount of markup, and simply let the "repeater" repeat that markup over and over.
I would also consider a GridView, or more flexible would be to consider a ListView. These controls allow hiding of the PK value, and for a lot of repeating data displays (such as grid’s and tables), then not exposing the PK from the database is a welcome security measure.
Also, since the button is nested inside of the Repeater control, then a simple double click on the button (in the designer view) will not create the code behind (click) stub for you. Hence, you have to do this in markup, but intel-sense will and can still create the click event for you. You can do this by entering OnClick=" (then hit space bar, and you given a choice to select a existing event, or create a new one).
The process looks like this:
So, when you hit "=" in the markup, then intel-sense should offer you to create the click event for that button. When you now flip back to code behind view, you find that button click code stub having been created for you.
Hence, for nested controls inside of data bound controls, then you can (should) use the above approach to create a click event for controls inside of data bound controls.
For buttons not nested inside such controls, then a simple double click on such buttons in design view will suffice to create the click event.