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);
Related
My code-
public void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
Form2 f2 = new Form2(id);
f2.textBox1.Text = this.dataGridView1.CurrentRow.Cells[0].Value.ToString();
f2.textBox2.Text = this.dataGridView1.CurrentRow.Cells[1].Value.ToString();
f2.textBox3.Text = this.dataGridView1.CurrentRow.Cells[2].Value.ToString();
f2.textBox4.Text = this.dataGridView1.CurrentRow.Cells[3].Value.ToString();
f2.Show();
this.Hide();
}
public void dataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
Form2 f2 = new Form2(id);
f2.textBox1.Text = this.dataGridView1.CurrentRow.Cells[0].Value.ToString();
f2.textBox2.Text = this.dataGridView1.CurrentRow.Cells[1].Value.ToString();
f2.textBox3.Text = this.dataGridView1.CurrentRow.Cells[2].Value.ToString();
f2.textBox4.Text = this.dataGridView1.CurrentRow.Cells[3].Value.ToString();
f2.Show();
this.Hide();
}
my gui-
My problem is that when I click on the datagrid tuple it is not going to the next form.
It is just freezing.
edit-
this how I am filling the datagrid -
CheckBox[] Locations = { checkBox1, checkBox2, checkBox3 };
CheckBox[] Profiles = { checkBox4, checkBox5, checkBox6 };
string locs = string.Join(" or ", Locations.Where(c => c.Checked).Select(x => $"location = '{x.Text}'"));
string profs = string.Join(" or ", Profiles.Where(c => c.Checked).Select(x => $"profile = '{x.Text}'"));
MessageBox.Show(locs);
string query = $"select * from jobs where ({locs}) and profile in(select profile from jobs where ({profs}))";
OracleCommand comm2 = new OracleCommand(query, conn);
OracleDataAdapter MyAdapter = new OracleDataAdapter();//adapter acts as interface btw database and dataset(which is collectio of tables)
MyAdapter.SelectCommand = comm2;
DataTable dTable = new DataTable();//datatable represents a single table in database
MyAdapter.Fill(dTable);
dataGridView1.DataSource = dTable;
this might help in debugging.So my query basically returns a row of tuples and I will click any of the rows and it should go to another form where the column values of that row are stored in the textbox of the second form.
If you take a closer look at CellContentClick event, you'll see that this event:
Occurs when the content within a cell is clicked.
I just tried your posted code, and it works. It's a bit tricky though using CellContentClick event because you cursor has to click on the content of the cell exactly. I advise you to use CellClick event instead.
This event:
Occurs when any part of a cell is clicked.
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
Form2 f2 = new Form2(id);
f2.textBox1.Text = this.dataGridView1.CurrentRow.Cells[0].Value.ToString();
f2.textBox2.Text = this.dataGridView1.CurrentRow.Cells[1].Value.ToString();
f2.textBox3.Text = this.dataGridView1.CurrentRow.Cells[2].Value.ToString();
f2.textBox4.Text = this.dataGridView1.CurrentRow.Cells[3].Value.ToString();
f2.Show();
this.Hide();
}
Update:
Since you're telling me that by adding this code, it also didn't work. You need to make sure that your DataGridView has been subscribed to those events:
Open you form in design view.
Click on your DataGridView
Press F4.
Now check the events. They should contain the name of the function you added. Like this:
If you prefer to do this programatically, you can add this code in the Form.Load event:
dataGridView1.CellClick += dataGridView1_CellClick;
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;
}
}
I need to use a single event handler for multiple buttons. I generated those buttons via while loop according to the database query. I created a single method
void MyButtonClick(object sender, EventArgs e)
{
}
I'm new to the C#. How can I bind all button's events to a single handler.
Code for generating the buttons:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
MydbConnection db = new MydbConnection();
MySqlConnection con = db.connection();
MySqlCommand cmd = new MySqlCommand();
cmd.CommandText = "select * from categories where online = 1";
cmd.Connection = con;
MySqlDataReader rd;
con.Open();
rd = cmd.ExecuteReader();
int i = 1;
while (rd.Read())
{
Button btn = new Button();
btn.Name = "btn-" + i.ToString();
btn.Tag = i;
btn.Text = rd.GetString(2).ToString();
btn.Height = 60;
btn.Width = 100;
btn.Location = new Point(900, 60 * i + 10);
this.Controls.Add(btn);
i++;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
Welcome to SO. You can add this right before this.Controls.Add(btn);
btn.Click += MyButtonClick;
You need to set all of their CLICK properties to the same event handler:
btn.Click += new EventHandler(MyButtonClick);
That should make it so that anytime you click any of the buttons, MyButtonClick() is the event that triggers. Of course, if you need to figure out WHICH button was clicked, then you need to check the sender parameter within the event, where 'i' is one of the number values you assigned to a button in the loop:
(if sender == btn-i) then ....
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.
I use this code to generate the data and output it in my gridview
string sql = "Sql Query";
string sqlCredit= "Sql Query";
string sqlCreditPayment = "Sql Query";
SqlDataAdapter da = new SqlDataAdapter();
DataSet ds = new DataSet();
ds.EnforceConstraints = false;
ds.DataSetName = "Receivables";
ds.Tables.Add((con.ShowResult(sql, ref da)).Tables[0].Copy());
ds.Tables[0].TableName = "dtReceivables";
ds.Tables.Add((con.ShowResult(sqlCredit, ref da)).Tables[0].Copy());
ds.Tables[1].TableName = "dtCredit";
ds.Tables[1].Columns[1].ColumnMapping = MappingType.Hidden;
ds.Tables[1].Columns[7].ColumnMapping = MappingType.Hidden;
ds.Tables.Add((con.ShowResult(sqlCreditPayment, ref da)).Tables[0].Copy());
ds.Tables[2].TableName = "dtCreditPayment";
ds.Tables[2].Columns[0].ColumnMapping = MappingType.Hidden;
DataRelation dr0 = new DataRelation("CreditList", ds.Tables[0].Columns["Id"], ds.Tables[1].Columns["DocSupplierId"]);
ds.Relations.Add(dr0);
DataRelation dr1 = new DataRelation("CreditPaymentList", ds.Tables[1].Columns["Id"], ds.Tables[2].Columns["SourceId"]);
ds.Relations.Add(dr1);
slipDashBoard.DataSource = ds.Tables["dtReceivables"];
slipDashBoard.ForceInitialize();
gridView1.BestFitColumns();
Guys. Pls help. i want to achieve something like this when i click on the gridview's children. thnx in advance
The main idea in this case is to obtain an instance of the GridView class which was clicked. XtraGrid creates clones of the pattern View which is created at design time and use these clones to disply data. Here is the code which should work:
GridView gridView = sender as GridView;
var value = gridView.GetRowCellValue(gridView.FocusedRowHandle, gridView.Columns["Num"));
MessageBox.Show(value.ToString());
Since your child GridView is created automatically, there are two approaches:
1) handle the GridControl's Click event handler:
private void gridControl1_Click(object sender, EventArgs e) {
GridControl grid = sender as GridControl;
Point p = new Point(((MouseEventArgs)e).X, ((MouseEventArgs)e).Y);
GridView gridView = grid.GetViewAt(p) as GridView;
if(gridView != null)
MessageBox.Show(gridView.GetFocusedRowCellDisplayText("Num"));
}
2) handle the GridView1 MasterRowExpanded event handler:
private void gridView1_MasterRowExpanded(object sender, CustomMasterRowEventArgs e) {
GridView master = sender as GridView;
GridView detail = master.GetDetailView(e.RowHandle, e.RelationIndex) as GridView;
detail.Click += new EventHandler(detail_Click);
}
void detail_Click(object sender, EventArgs e) {
GridView gridView = sender as GridView;
var value = gridView.GetRowCellValue(gridView.FocusedRowHandle, gridView.Columns["Num"));
MessageBox.Show(value.ToString());
}
If you create your grid on runtime you have an instance like gridview2. Now you can add the click event with gridview2.Click += new EventHandler(gridview2_Click);
Then you will get sth. like this:
void view_Click(object sender, EventArgs e)
{
//take the code from platons post...
}