Right now I've got a method that's dynamically generating a table of information based on input in a search bar, with a button located on each row. When pressed, that button is supposed to take the information present in its row and process it.
My problem is that my buttons aren't doing anything, but I'm not exactly sure how to fix it. Even trying to post to the output panel in VS doesn't work. As soon as I press one, all it does is wipe out all of my values - everything in my table - all back to default by refreshing the page.
// The bottom section of the method which 'generates' buttons for each row made in the for loop - everything works perfect to this point
TableCell addButtonCell = new TableCell();
Button addButton = new Button();
addButton.Text = "Add";
addButton.Width = 75;
addButton.Click += new EventHandler(addButton_Click);
addButtonCell.Controls.Add(addButton);
row.Cells.Add(addButtonCell);
tblResults.Rows.Add(row);
}
// button event handler
public void addButton_Click(object sender, EventArgs e) {
System.Diagnostics.Debug.Write("TESTING!!!!!"); // Tried this, no effect
}
Whatever controls are created Dynamically, needs to be created each time the page loaded.
SOLUTION:
You need to do the way I done in the following...
TableRow row;
TableCell cell;
protected void Page_Load(object sender, EventArgs e)
{
if (this.IsPostBack)
{
this.ReloadDynamicControls();
}
else
{
DynamicControls = new List<Button>();
}
}
private void ReloadDynamicControls()
{
if (this.DynamicControls != null)
{
foreach (Button button in DynamicControls)
{
button.Click += new EventHandler(btnKill_Click);
row = new TableRow();
cell = new TableCell();
cell.Controls.Add(button);
row.Controls.Add(cell);
Panel1.Controls.Add(row);
}
}
}
private List<Button> DynamicControls
{
get { return Session["DynamicControls"] != null ? (List<Button>)Session["DynamicControls"] : null; }
set { Session["DynamicControls"] = value; }
}
And you need change you generate button method with this code:
TableRow tr = new TableRow();
tblResults.Controls.Add(tr);
Button addButton = new Button();
addButtonCell = new TableCell();
addButtonCell.Controls.Add(addButton);
tr.Controls.Add(addButtonCell);
addButton.Width = 75;
addButton.Text = "Add";
addButton.ID = "btn_" + this.DynamicControls.Count.ToString();
addButton.Click += new EventHandler(addButton_Click);
addButton.EnableViewState = true;
DynamicControls.Add(addButton);
tr.TableSection = TableRowSection.TableBody;
tblResults.Rows.Add(tr);
The original solution here
You'd wanna include here that the query should have a WHERE clause and should be limitted by 1, ensuring that actually one result is returned. then the if should also be changed to if($result->num_rows == 0).
Related
(Original text updated!)
I am slowly getting into ASP.NET, currently trying to make a small webforms app for a hackathon.
The page that I am having problems with is supposed to show a list of companies to which person can apply.
Since the list of those companies can change, company always can be added or removed, I want to make some kind of table which loads values dynamically. The table appears when a user clicks "View all companies", it shows a table with each row including the name of company and a button to move to the page of this company. At this stage, I am trying to make something even more simple, for the sake of testing.
So, the button should just display some kind of text.
But here's the problem.
When I click on the button, it just resets the page and as I understand, runs "Page_Load". The text is also resets to the value from Page_Load.
Now, I'll try to give code and try to give the best descriptions.
Here's the Page_Load, on first load it just displays the name of the user, and hides the table with company names. On non-first load, it should run click button events.
protected void Page_Load(object sender, EventArgs e)
{
HRGlobalHub.Code.Start.Firebase.thisFirebaseClient = new FireSharp.FirebaseClient(HRGlobalHub.Code.Start.Firebase.thisFirebaseConfig);
pnlCompaniesView.Visible = false;
if (!IsPostBack)
{
if (thisUser == null)
lblWelcomeUser.Text = "Welcome";
else
lblWelcomeUser.Text = "Welcome, " + thisUser.FirstName + " " + thisUser.LastName;
gCompany = new Code.Company.Company();
}
else
{
if (Action=="Apply")
btnApplyClick(this, e);
else if (Action == "Suggest")
btnSuggestClick(this, e);
if (Action == "View")
btnViewClick(this, e);
}
}
The code for showing all companies comes here:
protected async void btnViewAllCompanies_Click(object sender, EventArgs e)
{
ResetCompaniesTable();
lblWelcomeUser.Text = "ViewAll";
FirebaseResponse response = await HRGlobalHub.Code.Start.Firebase.thisFirebaseClient.GetTaskAsync("HRGlobalHub/Database/Companies/CompaniesList/");
HRGlobalHub.Code.Company.CompaniesNamesList thisData = response.ResultAs<HRGlobalHub.Code.Company.CompaniesNamesList>();
string[] CompanyArray = thisData.CompanyNameListVar.Split(',');
for (int i = 1; i < CompanyArray.Length; i++)
{
response = await HRGlobalHub.Code.Start.Firebase.thisFirebaseClient.GetTaskAsync("HRGlobalHub/Database/Companies/CompaniesDatabase/" + CompanyArray[i]);
HRGlobalHub.Code.Company.Company result = response.ResultAs<HRGlobalHub.Code.Company.Company>();
tbtTestTable.Rows.Add(NewCompanyRow(result));
}
pnlCompaniesView.Visible = true;
pnlCompaniesView.Width = 1100;
tbtTestTable.Width = 1100;
lblWelcomeUser.Text = tbtTestTable.Visible.ToString();
}
This part loads first just the names of companies, then full companies data from the server. Each company turns into a table row. It also creates OnClientClick button events, that should load on non-first Page Load.
public TableRow NewCompanyRow(HRGlobalHub.Code.Company.Company thisCompany)
{
TableRow newRow = new TableRow();
Button btnApply = new Button();
btnApply.Text = "Apply";
btnApply.OnClientClick += gCompany = thisCompany;
btnApply.OnClientClick += Action = "Apply";
Button btnSuggest = new Button();
btnSuggest.Text = "Suggest";
btnSuggest.OnClientClick += gCompany = thisCompany;
btnSuggest.OnClientClick += Action = "Suggest";
Button btnView = new Button();
btnView.Text = "View";
btnView.OnClientClick += gCompany = thisCompany;
btnView.OnClientClick += Action = "View";
TableCell[] Cells = new TableCell[6];
for (int i = 0; i < Cells.Length; i++)
Cells[i] = new TableCell();
Cells[0].Text = thisCompany.Name;
Cells[1].Text = thisCompany.FieldOfWork;
Cells[2].Text = thisCompany.Employees.Count.ToString();
Cells[3].Controls.Add(btnView);
Cells[4].Controls.Add(btnApply);
Cells[5].Controls.Add(btnSuggest);
for (int i = 0; i < Cells.Length; i++)
Cells[i].Width = 150;
newRow.Width = 900;
for (int i = 0; i < Cells.Length; i++)
newRow.Cells.Add(Cells[i]);
btnApply.Width = 150;
btnSuggest.Width = 150;
btnView.Width = 150;
return newRow;
}
Again, since it's just testing stage for this page, the button should only display the name of the company that it gets as variable.
void btnApplyClick(Object sender, EventArgs e, HRGlobalHub.Code.Company.Company thisCompany)
{
lblWelcomeUser.Text = "Apply to " + thisCompany.Name;
}
This works but only partly: whatever I try the code from Page Load always runs the last "mentioned" Action (which is "View") and always goes for the company from the last row.
Please help me solve this!
Evgenie
Solved it myself after all. In the end, the error was, that the dynamically created buttons also had to exist\be created at Page Load. So, I added the void for creating table with buttons at the Page Load, and just turned it invisible.
And I also added the !PostBack proposed in comments, but on it's own it wasn't enough.
Here's where I found out about this:
CommandEventArgs and Event question
However that started causing problems with buttons that needs to transit to another page. So, I added one more variable that is also checked at Page Load. If it is false, the Page works usually, if true then it moves to another page.
protected void Page_Load(object sender, EventArgs e)
{
HRGlobalHub.Code.Start.Firebase.thisFirebaseClient = new FireSharp.FirebaseClient(HRGlobalHub.Code.Start.Firebase.thisFirebaseConfig);
pnlCompaniesView.Visible = false;
if (!IsPostBack)
{
if (thisUser == null)
lblWelcomeUser.Text = "Welcome";
else
lblWelcomeUser.Text = "Welcome, " + thisUser.FirstName + " " + thisUser.LastName;
}
else if (Transit == true)
{
HRGlobalHub.Pages.Company.CreateCompany.thisUser = thisUser;
Server.Transfer("/Pages/Company/CreateCompany.aspx", false);
}
else if(Transit == false)
{
btnViewAllCompanies_Click(this, e);
pnlCompaniesView.Visible = false;
}
}
Buttons are created as before, no changes here:
Button btnApply = new Button();
btnApply.Text = "Apply";
btnApply.Click += (sender, EventArgs) => { btnApplyClick(sender, EventArgs, thisCompany); };
And the button that should transit to an other page, just changes the variable value:
protected void btnCreateCompany_Click(object sender, EventArgs e)
{
Transit = true;
}
I am trying to remove textboxes and labels one by one by pressing a button.
I have a list of textboxes called inputTextBoxes.
Here is the code for adding :
private void onClickAdd(object sender, EventArgs e)
{
inputTextBoxes = new List<TextBox>();
Label label1 = new Label();
label1.Name = "label1";
label1.Text = "w" + i;
label1.Location = new System.Drawing.Point(5, 10 + (20 * i));
label1.Size = new System.Drawing.Size(30, 20);
this.Controls.Add(label1);
TextBox text1 = new TextBox();
text1.Name = "text1";
text1.Location = new System.Drawing.Point(35, 10 + (20 * i));
text1.Size = new System.Drawing.Size(25, 20);
inputTextBoxes.Add(text1);
this.Controls.Add(text1);
i++;
}
For removing I am trying this :
private void onClickRemove(object sender, EventArgs e)
{
foreach(TextBox text1 in inputTextBoxes)
{
this.Controls.Remove(text1);
}
}
But it removes only the last textbox added,clicking againg on the button doesn't do anything.
You are constantly creating a new list in your OnClickAdd() method:
inputTextBoxes = new List<TextBox>();
Try to check if the inputTextBoxes is null and only then do this line of code. Otherwise, just let the rest of the code run.
Also, remember about clearing the inputTextBoxes list after the onClickRemove() method finishes removing textboxes/labels.
You want to remove only one TextBox at a time, why do you need a foreach loop? just grab the last or first TextBox and if it is not null remove it from the Controls:
private void onClickRemove(object sender, EventArgs e)
{
var textBoxToRemove = inputTextBoxes.LastOrDefault();
// or
// var textBoxToRemove = inputTextBoxes.FirstOrDefault();
if (textBoxToRemove != null)
{
this.Controls.Remove(textBoxToRemove);
inputTextBoxes.Remove(textBoxToRemove);
}
}
Make sure you remove it from inputTextBoxes also so the next time you will ask to remove a TextBox it will not try to remove it again and go on to the next one.
Edit
#Piotr Nowak has pointed one more problem you have, you allocate a new list for inputTextBox every time you add a new TextBox, you should allocate the list only once when you create your class.
Remove this from onClickAdd method:
inputTextBoxes = new List<TextBox>();
And use this when you declare the list as a field it your class:
private readonly inputTextBoxes = new List<TextBox>();
I am generating the text boxes dynamically.Table rows are created dynamically too, and these text boxes are added to those dynamically created row cells and they are added to the table using the following code
protected override void OnInit(EventArgs e)
{
PopulateTextBoxes();
SetFocus();
base.OnInit(e);
}
protected void PopulateTextBoxes()
{
int quantityRequired = 0;
quantityRequired =GetQuantity();
for (int j = 0; j < quantityRequired; j++)
{
TableRow row = new TableRow();
TableCell cell1 = new TableCell();
TextBox tb = new TextBox();
tb.ID = j.ToString() +"_RowTbx"
tb.AutoPostBack = true;
tb.TextChanged += new EventHandler(tb_TextChanged);
cell1.Controls.Add(tb);
row.Cells.Add(cell1);
TableCell cell2 = new TableCell();
CheckBox chBox = new CheckBox();
chBox.CheckedChanged += new EventHandler(chBox_CheckedChanged);
chBox.AutoPostBack = true;
cell2.Controls.Add(chBox);
row.Cells.Add(cell2);
TableCell cell3 = new TableCell();
Image img = new Image();
img.Width = Unit.Pixel(25);
img.Height = Unit.Pixel(25);
img.ImageUrl = "HttpRuntime.AppDomainAppVirtualPath" + "/Images/" +"img.jpeg";
cell3.Controls.Add(img);
row.Cells.Add(cell3);
tbl_Serial.Rows.Add(row);
}
LoadDataIfExists();
}
private void tb_TextChanged(object sender, EventArgs e)
{
//I have implemented code to validate the text entered in the text box.
}
protected void SetFocus()
{
int emptytbxRow = 0;
TextBox tbx = new TextBox();
for (int i = 0; i < tbl_Serial.Rows.Count; i++)
{
string tbxId = i.ToString() + "_RowTbx";
string text = ((TextBox)tbl_Serial.Rows[i].Cells[0].FindControl(tbxId))).Text;
if (text == null || text==string.Empty)
{
tbx=((TextBox)(tbl_Serial.Rows[i].Cells[0].FindControl(tbxId)));
if (tbx != null)
tbx.Focus();
}
}
protected void LoadDataIfExists()
{
List<string> lstData=Service.GetData(int someNum)
for (int j = 0; j < lstData.Count; j++)
{
string tbxID = j.ToString() + "_RowTbx";
TextBox tbx = (TextBox)tbl_Serial.Rows[j].Cells[0].FindControl(tbxID);
tbx.Text = lstData[j];
}
}
When I debug, the tbx.focus seems to hit rightly but i do not see the cursor blinking on the text box in my UI.I do not know if I am missing something imp. Thank you.
Edit: Sorry I was not clear. When the page loads, the text boxes may contain data, but not all text boxes contain data. So whenever the page loads there are a few text boxes with data and there are empty ones. I want the cursor to be at the first empty box.
Given that you know which text box is your first empty one you want to also be aware of the page life cycle. OnInit is to early in the page life cycle for this to occur as the page is still initializing and the objects haven't yet rendered to the form. Try OnLoad or use PreRender to set focus to your item right before the form is rendered.
The link below will show you all available methods that you can hook into during the cycle.
ASP Page Lifecycle
For some unknown reason , C#code with the same logic as below did not work, while javascript works. Hope the following helps someone in future. Thank you Liqua for providing the start.
window.onload = function () {
FindWhichTextBoxIsEmpty();
}
function FindWhichTextBoxIsEmpty() {
var tableSerial = document.getElementById('tbl_Serial');
for (var i = 0; i < tableSerial.rows.length-1; i++) {
var ID = i.toString() + "_RowTbx";
if (document.getElementById(ID).value!="") {
var tb = document.getElementById(ID).value;
if (tb != "") {
if (i + 1 < tableSerial.rows.length-1) {
var nextID = (i + 1).toString() + "_RowTbx";
document.getElementById(nextID).focus();
}
}
}
}
}
didn't got your question clearly, what i got is you created a textbox dynamically, and trying to focus it at runtime.. May be this work for you try creating tb += getFocus event, or try tb.Focus();
I created dropdownlist at runtime when a button is clicked.and i palced another button to get the selected text from dynamic dropdownlist.When i try to retrieve the selected text from dropdownlist it gives me the error called object reference not set, following is my code.
TableRow tr;
TableCell tc;
DropDownList dp;
TextBox txt;
protected void Button1_Click(object sender, EventArgs e)
{
int no = int.Parse(TextBox1.Text);
for (int i = 0; i < no; i++)
{
tr = new TableRow();
tr.BorderStyle = BorderStyle.Groove;
for (int j = 0; j < 1; j++)
{
tc = new TableCell();
tc.BorderStyle = BorderStyle.Groove;
dp = new DropDownList();
//form1.Controls.Add(dp);
txt = new TextBox();
dp.Items.Add("hello");
tc.Controls.Add(dp);
tc.Controls.Add(txt);
tr.Cells.Add(tc);
}
Table1.Rows.Add(tr);
}
}
protected void Button2_Click(object sender, EventArgs e)
{
TextBox1.Text =((DropDownList)this.FindControl("dp")).SelectedItem.Text;
}
You can't do it this way. Remember that on every request, you get a new page object, and new copies of all the controls in it. Any control you add dynamically must be added the same way every single time, otherwise it won't exist.
In this case, you add it once, when the button is clicked. When you click button2, a request is generated and a new page object is created that no longer has your dropdownlist, because it is only ever added in the button1 handler.
The easiest thing to do would be add your dropdownlist to the page normally but just set Visible to false. Then when they click button 1, set Visible to true. This will ensure that your dropdownlist will always be present.
Dynamic controls are tricky, and should be avoided when possible, especially if you're new to ASP.Net.
Actually, I was able to make it work..
I created a dataset ahead of the table creation, then:
tc = new TableCell();
dd= new DropDownList();
ddl.ID = dd1;
foreach (DataRow dr in dst.Tables[0].Rows)
{
ddl.Items.Add(new ListItem(dr["Text"].ToString(),dr["Value"].ToString()));
}
tcActions.Controls.Add(ddlActions);
I'm not an expert or anything, I just peck at it until I make it do what I want.
I've seen How to: Host Controls in Windows Forms DataGridView Cells which explains how to host a control for editing a cell in a DataGridView. But how can I host a control for displaying a cell?
I need to display a file name and a button in the same cell. Our UI designer is a graphic designer not a programmer, so I have to match the code to what he's drawn, whether it's possible - or wise - or not. We're using VS2008 and writing in C# for .NET 3.5, if that makes a difference.
UPDATE: The 'net suggests creating a custom DataGridViewCell which hosts a panel as a first step; anyone done that?
As per your "UPDATE", creating a custom DataGridViewCell is the way this is done. I've done it, and it doesn't require that much modification from the example code available from the MSDN. In my case, I needed a bunch of custom editing controls, so I ended up inheriting from DataGridViewTextBoxCell and DataGridViewColumn. I inserted into my class (the one inherited from DataGridViewTextBoxCell) a new custom control which implemented IDataGridViewEditingControl, and it all just worked.
I suppose that in your case, you could write a PanelDataGridViewCell which would contain a control MyPanelControl which would inherit from Panel and implement IDataGridViewEditingControl.
Rather than use a datagridview, how about using a TableLayoutPanel instead. Create your control that has a label and a button and events and fill your layout panel with them. Your control becomes the cell so to speak. It doesn't take much to make the table layout panel to look like a datagridview, if that is the layout style you want.
There are two ways to do this:
1). Cast a DataGridViewCell to a certain cell type that exists. For example, convert a DataGridViewTextBoxCell to DataGridViewComboBoxCell type.
2). Create a control and add it into the controls collection of DataGridView, set its location and size to fit the cell that to be host.
See Zhi-Xin Ye's sample code below which illustrates the tricks:
private void Form_Load(object sender, EventArgs e)
{
DataTable dt = new DataTable();
dt.Columns.Add("name");
for (int j = 0; j < 10; j++)
{
dt.Rows.Add("");
}
this.dataGridView1.DataSource = dt;
this.dataGridView1.Columns[0].Width = 200;
/*
* First method : Convert to an existed cell type such ComboBox cell,etc
*/
DataGridViewComboBoxCell ComboBoxCell = new DataGridViewComboBoxCell();
ComboBoxCell.Items.AddRange(new string[] { "aaa","bbb","ccc" });
this.dataGridView1[0, 0] = ComboBoxCell;
this.dataGridView1[0, 0].Value = "bbb";
DataGridViewTextBoxCell TextBoxCell = new DataGridViewTextBoxCell();
this.dataGridView1[0, 1] = TextBoxCell;
this.dataGridView1[0, 1].Value = "some text";
DataGridViewCheckBoxCell CheckBoxCell = new DataGridViewCheckBoxCell();
CheckBoxCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
this.dataGridView1[0, 2] = CheckBoxCell;
this.dataGridView1[0, 2].Value = true;
/*
* Second method : Add control to the host in the cell
*/
DateTimePicker dtp = new DateTimePicker();
dtp.Value = DateTime.Now.AddDays(-10);
//add DateTimePicker into the control collection of the DataGridView
this.dataGridView1.Controls.Add(dtp);
//set its location and size to fit the cell
dtp.Location = this.dataGridView1.GetCellDisplayRectangle(0, 3,true).Location;
dtp.Size = this.dataGridView1.GetCellDisplayRectangle(0, 3,true).Size;
}
MSDN Reference : how to host different controls in the same column in DataGridView control
Using the 1st method looks like this:
Using the 2nd method looks like this:
Additional info: Controls in the same DataGridView column dont render while initializing grid
Usually you should host controls in winfors forms datagridview cells as shown here
But if you need your control to be always visible, what you can do is make a custom column inheriting from DataGridViewImageColumn. Add the control to the datagridview. Set the DefaultCellStyle.Nullvalue of the control to a bitmap of the control you want always shown on the data gridview. Then using the cellMouseEnter event you can display and reposition the control to display over the image cell. This gives the appearance that your custom control is always visible without using as much resources as creating a new instance of your control for every row added to the datagridview. This will help performance quite a bit.
Here is what I did with my custom “AddRemove” usercontrol.
public class AddRemoveColumn : DataGridViewImageColumn
{
private AddRemove SelectionControl = null;
private Bitmap SelectionControlImage = null;
public AddRemoveColumn()
{
SelectionControl = new AddRemove();
}
#region Set Up Column
protected override void OnDataGridViewChanged()
{
base.OnDataGridViewChanged();
if (DataGridView != null)
{
Activate();
}
}
private void Activate()
{
SelectionControl.LostFocus += SelectionControl_LostFocus;
this.DataGridView.CellMouseEnter += DataGridView_CellMouseEnter;
this.DataGridView.BackgroundColorChanged += DataGridView_BackgroundColorChanged;
this.DataGridView.RowHeightChanged += DataGridView_RowHeightChanged;
SelectionControl.OnAddClicked += AddClicked;
SelectionControl.OnRemoveClicked += RemoveClicked;
this.DataGridView.Controls.Add(SelectionControl);
SelectionControl.Visible = false;
this.Width = SelectionControl.Width;
SelectionControl.BackColor = this.DataGridView.BackgroundColor;
this.DataGridView.RowTemplate.Height = SelectionControl.Height +1;
foreach (DataGridViewRow row in DataGridView.Rows)
{
row.Height = SelectionControl.Height+1;
}
SetNullImage();
}
#endregion
private void AddClicked(int RowIndex)
{
MessageBox.Show("Add clicked on index=" + RowIndex.ToString());
}
private void RemoveClicked(int RowIndex)
{
MessageBox.Show("Removed clicked on index=" + RowIndex.ToString());
}
private void SetNullImage()
{
if (SelectionControlImage != null)
{
SelectionControlImage.Dispose();
}
SelectionControlImage = new Bitmap(SelectionControl.Width, SelectionControl.Height);
SelectionControl.DrawToBitmap(SelectionControlImage, new Rectangle(0, 0, SelectionControlImage.Width, SelectionControlImage.Height));
this.DefaultCellStyle.NullValue = SelectionControlImage;
}
private void DataGridView_RowHeightChanged(object sender, DataGridViewRowEventArgs e)
{
if (e.Row.Height <= 40)
{
e.Row.Height = 40;
}
SelectionControl.Visible = false;
SetPosition(Index, e.Row.Index);
}
private void DataGridView_BackgroundColorChanged(object sender, EventArgs e)
{
SelectionControl.BackColor = this.DataGridView.BackgroundColor;
SetNullImage();
}
private void SelectionControl_LostFocus(object sender, EventArgs e)
{
SelectionControl.Visible = false;
}
private void SetPosition(int ColumnIndex, int RowIndex)
{
Rectangle celrec = this.DataGridView.GetCellDisplayRectangle(ColumnIndex, RowIndex, true);//.Rows[e.RowIndex].Cells[e.ColumnIndex].GetContentBounds();
int x_Offet = (celrec.Width - SelectionControl.Width)/ 2;
int y_Offet = (celrec.Height - SelectionControl.Height)/2;
SelectionControl.Location = new Point(celrec.X + x_Offet, celrec.Y + y_Offet);
SelectionControl.Visible = true;
SelectionControl.RowIndex = RowIndex;
}
private void DataGridView_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == this.Index)
{
SetPosition(e.ColumnIndex, e.RowIndex);
}
}
}