Problems with dynamic buttons in ASP.NET (C#) - c#

I have a search function which looks up specific users in my OleDb database. For each user, a row in a table is created containing his name, boolean saying if he is an admin or not and a delete button. The delete button is not working as expected, it doesn't execute the method that is linked to it. My code is located in the Search_Click event which executes right after the admin clicks the search button to search for specific users.
I tried to place a pre-defined button from my code but in Page_Load instead and it works as expected. How can I get the button to work from the Search_Click as well?
Basically, after dynamically creating the LinkButtons according to the user search text and clicking the search button (Search_Click) I need to find a way to register the click event on page load event, I do not know how to do that because the linkbuttons HAVE to be created in the click event of the search button but they also have to be registered via page load.
using (OleDbDataReader reader = cmd.ExecuteReader())
{
Table table = new Table();
TableRow row = new TableRow();
TableCell cell = new TableCell();
Label label = new Label();
while (reader.Read())
{
row = new TableRow();
cell = new TableCell();
label = new Label();
label.Text = reader.GetString(0);
cell.Controls.Add(label);
cell.BorderStyle = BorderStyle.Solid;
row.Cells.Add(cell);
cell = new TableCell();
label = new Label();
label.Text = reader.GetBoolean(1).ToString();
cell.Controls.Add(label);
cell.BorderStyle = BorderStyle.Solid;
row.Cells.Add(cell);
cell = new TableCell();
LinkButton button = new LinkButton();
button.Text = "Delete";
button.ID = (string) reader["uName"];
button.CommandName = (string)reader["uName"];
button.Click += new EventHandler(DeleteUser);
cell.Controls.Add(button);
cell.BorderStyle = BorderStyle.Solid;
row.Cells.Add(cell);
table.Rows.Add(row);
}
table.Style.Add(HtmlTextWriterStyle.MarginLeft, "auto");
table.Style.Add(HtmlTextWriterStyle.MarginRight, "auto");
TableHolder.Controls.Add(table);
}
DeleteUser:
protected void DeleteUser(object sender, EventArgs e)
{
try
{
LinkButton button = (LinkButton)sender;
string path = Server.MapPath(#"App_Data/ArcadeDatabase.accdb");
using (OleDbConnection con = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + path + "; Persist Security Info = False;"))
{
try
{
con.Open();
}
catch(Exception ex)
{
helper.Log("OleDbError", ex.Message);
}
if(con.State == System.Data.ConnectionState.Open)
{
using (OleDbCommand cmd = new OleDbCommand("DELETE * FROM ArcadeDatabase WHERE uName ='" + button.CommandName + "';", con))
{
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
helper.Log("OleDbError", ex.Message);
}
}
}
con.Dispose();
}
path = "";
}
catch (Exception ex)
{
helper.Log("Admin", ex.Message);
}
}

The event handler for dynamic controls need to be registered during Page_Load / Page_Init events to be triggered (see this link).
So, You have to add your Delete button and its event handler in page_load or init. to find ID of the control which cause postback in Page_Init event, see this
UPDATE:
OK, i add this code to proof it can be done without problems.
To see how it works: create a new Web Form and paste the following HTML & Code-Behinds into it. Then run the app and type some name in Search TextBox (eg: John or Ali) and press Search Button to filter the results.
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
string searchName = "";
if (SearchTextBox.Text != "") //if (this.GetPostBackControlUniqueID() == Search.UniqueID) //Search button is clicked!
{
searchName = SearchTextBox.Text;
}
addTable(searchName);
}
void addTable(string searchName)
{
string[] data = new[] {
"John, false, usrJohn"
,"Alex, false, usrAlex"
,"Ali, true, usrAli"
};
//using (OleDbDataReader reader = cmd.ExecuteReader())
//{
Table table = new Table(); table.CellPadding = 10;
TableRow row;
TableCell cell;
Label label;
foreach(string dataItem in data) //while (reader.Read())
{
string[] reader = dataItem.Split(',');
if (reader[0].IndexOf(searchName, StringComparison.OrdinalIgnoreCase) < 0) continue; //search not found (in real app, you use sql where clause for this, but this is just for test)
row = new TableRow();
cell = new TableCell();
label = new Label();
label.Text = reader[0].Trim(); //reader.GetString(0);
cell.Controls.Add(label);
cell.BorderStyle = BorderStyle.Solid;
row.Cells.Add(cell);
cell = new TableCell();
label = new Label();
label.Text = reader[1].Trim(); //reader.GetBoolean(1).ToString();
cell.Controls.Add(label);
cell.BorderStyle = BorderStyle.Solid;
row.Cells.Add(cell);
cell = new TableCell();
LinkButton button = new LinkButton();
button.Text = "Delete";
string uName = reader[2].Trim();
button.ID = uName; //(string)reader["uName"];
button.CommandName = uName; //(string)reader["uName"];
button.Click += new EventHandler(DeleteUser);
cell.Controls.Add(button);
cell.BorderStyle = BorderStyle.Solid;
row.Cells.Add(cell);
table.Rows.Add(row);
}
table.Style.Add(HtmlTextWriterStyle.MarginLeft, "auto");
table.Style.Add(HtmlTextWriterStyle.MarginRight, "auto");
TableHolder.Controls.Add(table);
//} //end using OleDbDataReader reader
}
protected void Search_Click(object sender, EventArgs e)
{
//addTable(SearchTextBox.Text); //already done in Page_Load
}
protected void DeleteUser(object sender, EventArgs e)
{
LinkButton button = (LinkButton)sender;
//using (OleDbCommand cmd = new OleDbCommand("DELETE * FROM ArcadeDatabase WHERE uName ='" + button.CommandName + "';", con))
string sql = "DELETE * FROM ArcadeDatabase WHERE uName ='" + button.CommandName + "';";
//execute the sql ... (in real app)
TableHolder.Controls.Add(new LiteralControl("The sql was: " + sql));
}
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = DateTime.Now.ToString("HH:mm:ss");
}
} //end class
And this is the helper extension method to Get UniqueID of PostBack Control:
// ***********************************************************************
public static class MyWebPageExtensions
{
public static string GetPostBackControlUniqueID(this Page page)
{
if (!page.IsPostBack)
return string.Empty;
Control control = null;
string controlName = page.Request.Params["__EVENTTARGET"]; //this method works only for link buttons which use javascript: __doPostBack(...) and not input type=submit buttons. Note: it is also available in Request.Form collection but this doesn't require to loop over Form.
if (!String.IsNullOrEmpty(controlName))
{
control = page.FindControl(controlName);
}
else
{
// __EVENTTARGET is null, the control is a button (not javascript linkButton), we need to iterate over the form collection to find it
foreach (string id in page.Request.Form)
{
// handle ImageButton they having an additional "quasi-property" in their Id which identifies mouse x and y coordinates
if (id.EndsWith(".x") || id.EndsWith(".y"))
{
string id2 = id.Substring(0, id.Length - 2);
control = page.FindControl(id2);
}
else
{
control = page.FindControl(id);
}
if (control is IButtonControl) break;
}
}
return control == null ? String.Empty : control.UniqueID;
}
}

Related

Making C# code generate webform with Events

protected void Page_Load(object sender, EventArgs e)
{
if (Request.QueryString.Count > 0)
{
DataTable dt = new DataTable();
SqlDataAdapter adapter = new SqlDataAdapter("select * from personalization where ponumber = '" + Request.QueryString["PO"] + "'", VisualConnect);
adapter.Fill(dt);
if (dt.Rows.Count > 0)
{
for (Int32 i = 0; i < dt.Rows.Count; i++)
{
HtmlGenericControl tr = new HtmlGenericControl("tr");
HtmlGenericControl td = new HtmlGenericControl("td");
HtmlGenericControl td1 = new HtmlGenericControl("td");
Label lbcustomename = new Label();
lbcustomename.ID = "lbline1";
lbcustomename.Text = "Recipient Name: ";
td.Controls.Add(lbcustomename);
tr.Controls.Add(td);
TextBox txtcustombox = new TextBox();
txtcustombox.ID = "txtline1";
txtcustombox.Text = dt.Rows[i]["line1"].ToString();
td1.Controls.Add(txtcustombox);
tr.Controls.Add(td1);
placeholder.Controls.Add(tr);
//Add button after last record
if (i == dt.Rows.Count - 1)
{
tr = new HtmlGenericControl("tr");
td = new HtmlGenericControl("td");
Button btnSubmit = new Button();
btnSubmit.ID = "btnSubmit";
btnSubmit.Click += btnSubmit_Click;
btnSubmit.Text = "Submit";
btnSubmit.Attributes.Add("runat", "server");
td.Controls.Add(btnSubmit);
td.Attributes.Add("Colspan", "2");
td.Attributes.Add("style", "text-align:center;");
tr.Controls.Add(td);
placeholder.Controls.Add(tr);
}
}
}
}
}
private void btnSubmit_Click(object sender, EventArgs e)
{
// do something
}
}
I have added this code to dynamically draw up fields from a database for input where the user would click the Submit button when completed. However, whenever I click the submit button, it wants to reload the page and the btnSubmit_Click event is never invoked instead. Am I missing something?

Filtering combo box items using text search in C# windows form

I'm Trying to filter a combo box using its text property in all characters of Items not only Beginning of them. I'm trying below code in TextChanged event of my combo box. but unfortunately combo box resets after entering any key:
private void cmbCompany_TextChanged(object sender, EventArgs e)
{
string QueryCompany = string.Format("select id,title from acc.dl where title LIKE '%" + cmbCompany.Text + "%' union select null , null order by title");
SqlDataAdapter DA1 = new SqlDataAdapter(QueryCompany, con);
DataTable DT1 = new DataTable();
DA1.Fill(DT1);
cmbCompany.DisplayMember = "Title";
cmbCompany.ValueMember = "id";
cmbCompany.DataSource = DT1;
}
connection string "con" is defined and opened.
thanks for your helps.
Below part of code which works for me where searching part works not only at the begining but also in the middle. I paste also additional part responsible for move focus to the next control on the form after you hit enter button.
Initialization part:
public Form1()
{
InitializeComponent();
cmbItems = new List<ComboBoxItem>();
InitializeComboBox();
}
private void InitializeComboBox()
{
Random rand = new Random();
for (int i = 0; i <= 1500; i++)
{
int counter = rand.Next(1, 105000);
cmbItems.Add(new ComboBoxItem("randomNumber" + counter, "ID_" + counter));
}
comboBox1.DataSource = cmbItems;
}
Filtering part:
private void comboBox1_TextUpdate(object sender, EventArgs e)
{
if (comboBox1.Text == string.Empty)
{
comboBox1.DataSource = cmbItems; // cmbItems is a List of ComboBoxItem with some random numbers
comboBox1.SelectedIndex = -1;
}
else
{
string tempStr = comboBox1.Text;
IEnumerable<ComboBoxItem> data = (from m in cmbItems where m.Label.ToLower().Contains(tempStr.ToLower()) select m);
comboBox1.DataSource = null;
comboBox1.Items.Clear();
foreach (var temp in data)
{
comboBox1.Items.Add(temp);
}
comboBox1.DroppedDown = true;
Cursor.Current = Cursors.Default;
comboBox1.SelectedIndex = -1;
comboBox1.Text = tempStr;
comboBox1.Select(comboBox1.Text.Length, 0);
}
}
Moving focus part:
private void comboBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar != '\r') return;
if (this.ActiveControl != null)
{
this.SelectNextControl(this.ActiveControl, true, true, true, true);
}
e.Handled = true; // Mark the event as handled
}
I hope that helps someone.
you can add a textbox to your form and use it's text to filter :
string QueryCompany =
string.Format(
"select id,title from acc.dl where dltype in (2,4) union select null , null order by title");
SqlDataAdapter DA1 = new SqlDataAdapter(QueryCompany, con);
con.Open();
DataTable DT1 = new DataTable();
DA1.Fill(DT1);
con.Close();
DataView dv1 = new DataView(DT1);
dv1.RowFilter = "Title like '%" + txtCompany.Text + "%' or Title is null";
cmbCompany.DisplayMember = "Title";
cmbCompany.ValueMember = "id";
cmbCompany.DataSource = dv1;
and in selected index changed event :
txtCompany.Text = cmbCompany.Text;

I programmatically create button that i place inside a table, i wish to add a click event to the button but failed

My Problems -
=> When i click the button, the page just refresh and the button gone. What i want is when i click the button, i can identify the button ID and show the message.
My GridView Code -
protected void initData()
{
DataTable dt = new DataTable();
DataColumn dc1 = new DataColumn("Button", typeof(Button));
dt.Columns.Add(dc1);
Button btn = new Button();
btn.ID = "Testing";
btn.Text = "Testing";
btn.Click +=btn_Click;
DataRow dr = dt.NewRow();
dr["Button"] = btn;
dt.Rows.Add(dr);
foreach (DataColumn col in dt.Columns)
{
BoundField bField = new BoundField();
bField.DataField = col.ColumnName;
bField.HeaderText = col.ColumnName;
gvTest.Columns.Add(bField);
}
gvTest.DataSource = dt;
gvTest.DataBind();
}
My code is as follow -
protected void initData()
{
var data = db.Courses;
TableRow tr = new TableRow();
TableCell tc = new TableCell();
Button btn = new Button();
btn.Text = "Testing";
btn.ID = "Testing";
btn.Click += btn_Click;
tc.Controls.Add(btn);
tr.Cells.Add(tc);
tbTest.Rows.Add(tr);
}
protected void btn_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
Response.Write(btn.ID);
}
Have you considered using a grid control:
http://msdn.microsoft.com/en-us/library/vstudio/bb907626(v=vs.100).aspx
That way, you don't have to write the HTML.
protected void GridView1_RowCommand(object sender,
GridViewCommandEventArgs e)
{
if (e.CommandName == "AddToCart")
{
// Retrieve the row index stored in the
// CommandArgument property.
int index = Convert.ToInt32(e.CommandArgument);
// Retrieve the row that contains the button
// from the Rows collection.
GridViewRow row = GridView1.Rows[index];
// Add code here to add the item to the shopping cart.
}
}
Hard to say without seeing all your code, but dynamically adding controls is almost always a PITA. One thing you have to make absolutely sure of is that you are recreating the controls on postback. The entire control tree must be identical in order for events and viewstate to work properly. So you need to be calling initData() as early as possible and you have to call it for postbacks as well, which is the opposite of what you do with typical ViewState-enabled controls declared in markup.

Event handler for dynamically created Button

I'm trying to convert a WinForms project to an ASP.Net project. Currently I'm struggling with a basic problem. I need to create a Button dynamically an display it on the page after the user selected a row in a GridView. Before I add the Button to the page I set an Click event handler. The problem is, that this event handler is never fired. I've tried to create the Button dynamically when the SelectedIndexChanged event of the GridView is fired and to create the Button as an instance member and set the event handler in the OnInit method of the class. Neither worked. Here is my code for the first attempt:
protected void dgvReports_SelectedIndexChanged(object sender, EventArgs e)
{
if (this.dgvReports.SelectedIndex >= 0)
{
Report rpt = (Report)bs.Current;
Control parameterCaption = this.divParameters.Controls[0];
Button btnAccept = new Button() { Text = "Get results" };
bool newLine = false;
this.divDescription.Visible = true;
this.divParameters.Visible = true;
this.divParameters.Controls.Clear();
this.divParameters.Controls.Add(parameterCaption);
this.txtDescription.Text = rpt.Description;
btnAccept.Click += new EventHandler(btnAccept_Click);
foreach (ReportParameter parameter in rpt.Parameters)
{
if (parameter.Visible)
{
this.divParameters.Controls.Add(new Label() { Text = parameter.Description, Width = 150, CssClass = "parameter" });
this.divParameters.Controls.Add(new TextBox() { Text = parameter.DefaultValue, Width = 300, ID = parameter.Name });
if (newLine)
{
this.divParameters.Controls.Add(new LiteralControl("<br />"));
}
newLine = !newLine;
}
}
this.divParameters.Controls.Add(new LiteralControl("<br /> <div style='text-align:center'>"));
this.divParameters.Controls.Add(btnAccept);
this.divParameters.Controls.Add(new LiteralControl("</div>"));
}
}
void btnAccept_Click(object sender, EventArgs e)
{
Report rpt = (Report)bs.Current;
SqlConnection con = new SqlConnection(global::System.Configuration.ConfigurationManager.ConnectionStrings["DP2ConnectionString"].ConnectionString);
SqlCommand com = new SqlCommand();
DataTable dataTable = new DataTable();
SqlDataAdapter sda = new SqlDataAdapter(com);
com.Connection = con;
com.CommandType = CommandType.StoredProcedure;
com.CommandText = rpt.DbProcedure;
dataTable.Locale = CultureInfo.CurrentCulture;
foreach (Control control in this.divParameters.Controls)
{
if (control is TextBox)
{
TextBox txt = control as TextBox;
com.Parameters.AddWithValue(txt.ID, txt.Text);
}
}
foreach (ReportParameter parameter in rpt.Parameters)
{
if (!parameter.Visible)
{
com.Parameters.AddWithValue(parameter.Name, parameter.DefaultValue);
}
}
sda.Fill(dataTable);
}
Dynamic controls in asp.net are never easy. Your event handler is probably not being included in the View State and therefore not persisting on postbacks, like when they click the button in question. The button has to be remade on each page load, which event handler being attached then as well- if possible, I'd save yourself a headache and try showing and hiding the button.
Try changing btnAccept.Click += new EventHandler(btnAccept_Click); to btnAccept.Click += new RoutedEventHandler(btnAccept_Click);

Button Event Handler Not Working

I am using the following lines of code for a button in C# :
void reserve_click(object sender, EventArgs e)
{
string req = ((Button)sender).ID;
}
protected void Button2_Click(object sender, EventArgs e)
{
issuedBooks.Visible = false;
search.Visible = true;
string text = TextBox1.Text;
string selectCommand = "SELECT id, title, author FROM book WHERE title LIKE '%" + text + "%' OR author LIKE '%" + text + "%'";
string conString = WebConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
SqlDataAdapter dad = new SqlDataAdapter(selectCommand, conString);
DataTable dtblCategories = new DataTable();
dad.Fill(dtblCategories);
DataView view = new DataView(dtblCategories);
foreach (DataRowView row in view)
{
TableRow newrow = new TableRow();
TableCell newcell1 = new TableCell();
TableCell newcell2 = new TableCell();
TableCell newcell3 = new TableCell();
newcell1.Text = row["title"].ToString();
newrow.Cells.Add(newcell1);
newcell2.Text = row["author"].ToString();
newrow.Cells.Add(newcell2);
string book_id = row["id"].ToString();
Button btn = new Button();
btn.ID = "Button_1" + book_id;
btn.Text = "Reserve";
btn.Click += new EventHandler(reserve_click);
newcell3.Controls.Add(btn);
newrow.Cells.Add(newcell3);
search.Rows.Add(newrow);
}
I am using the above code in a dynamically added button in a table cell. But the above EventHandler is not working or getting fired. I am using asp.net and C#for the first time. Can someone help me out ? Thanks.
here is the answer.Try it
Page_Load()
{
Button b = new Button();
b.ID = topic.Topic_Id + "_1"; // topic_Id is my unique ID for each topic on the blog
b.Text = "Edit";
b.ToolTip = "Edit";
b.CommandArgument = b.ID; //passing this to event handler
b.Command += new CommandEventHandler(b_Command); //handler
}
void b_Command(object sender, CommandEventArgs e)
{
System.Windows.Forms.MessageBox.Show(e.CommandArgument.ToString());
}

Categories