How to retain data for dynamically created controls? - c#

I have the following situation:
I have 2 drop down lists (DDL) with auto-postback enabled.
When the user selects something from first DDL, he can choose stuff from 2nd DDL.
When the user selects something from 2nd DDL, I open database (WHERE clause of SQL query is filled with values from DDLs), and dynamically create several text boxes, labels and buttons which I place inside a placeholder.
The problem is: when I click on some of the dynamically created buttons, nothing happens, and I can't retrieve data from dynamically created boxes. Also, placeholder content is lost (all controls).
So my question is: how do I retain data from dynamically created controls on postback?
I have listed a lot of similar articles on this site, and none solves my problem. Another thing is - I can't use view state nor session.
Code is:
public Button btn1 = new Button();
public TextBox txtBoxC1 = new TextBox();
public TextBox txtBoxC2 = new TextBox();
public TextBox txtBoxC3 = new TextBox();
...
...
...
protected void ddl2_SelectedIndexChanged(object sender, EventArgs e)
{
...
...
if some conditions are met:
...
...
String CS4 = ConfigurationManager.ConnectionStrings["connStringName"].ConnectionString;
using (SqlConnection conn4 = new SqlConnection(CS4))
{
SqlCommand cmd4 = new SqlCommand("Select * from table where column1=" +
Convert.ToInt32(ddl2.SelectedValue)+" order by column1", conn4);
conn4.Open();
SqlDataReader rdr = cmd4.ExecuteReader();
while (rdr.Read())
{
txtBoxC1.Text = rdr["column1"].ToString(); txtBoxC1.MaxLength = 3; txtBoxC1.Columns = 3; txtBoxC1.ID = ddl2.SelectedValue + "1";
txtBoxC2.Text = rdr["column2"].ToString(); txtBoxC2.MaxLength = 3; txtBoxC2.Columns = 3; txtBoxC2.ID = ddl2.SelectedValue + "2";
txtBoxC3.Text = rdr["column3"].ToString(); txtBoxC3.MaxLength = 3; txtBoxC3.Columns = 3; txtBoxC3.ID = ddl2.SelectedValue + "3";
btn1.Text = "click me"; btn1.ID = ddl1.SelectedValue; btn1.Click += btn1_Click;
phDynamic.Controls.Add(btn1);
phDynamic.Controls.Add(txtBoxC1);
phDynamic.Controls.Add(txtBoxC2);
phDynamic.Controls.Add(txtBoxC3);
}
}
}
...
...
...
private void btn1_Click(object sender, EventArgs e)
{
Response.Write(txtBoxC1.Text+txtBoxC2.Text+txtBoxC3.Text);
}
So instead of getting back values from my 3 textboxes, I don't get back anything, and I also lose placeholder controls.

Related

Change text of a TextBox clicking a Button with Controls created at run-time

I'm trying to change the text of a TextBox when I click a Button: both Controls are dynamically created as run-time.
The Buttons and the TextBoxes are created every time I click on another Button.
The Name Property for each control is specified by the User, using a TextBox.
For example, the user inputs "Test1", then the Button is named btn_Test1, and the TextBox is named txt_Test1.
The Button should open a FolderBrowserDialog and after a selection has been made, the TextBox shows the path selected.
I'm using the following code:
protected void button_Click(object sender, EventArgs e)
{
Button button = sender as Button;
folderBrowserDialog.ShowDialog();
string TextName = button.Name.Replace("btn_", "txt_");
TextBox selectText = new TextBox();
selectText = this.Controls[TextName] as TextBox;
selectText.Text = folderBrowserDialog.SelectedPath;
}
however this part gives me null:
selectText = this.Controls[TextName] as TextBox;
I did check with the debugger when I create the controls, so TextName is setting the correct Name.
The Buttons and TextBoxes are inserted in a TabControls, the Tab Name is set to the value the user inputs, so the main TabControl gets 2 controls.
I'm using a hidden TabControl named "TabFolders" that will be the main reference for creating tab clones
I'm using this code:
private void CreateDynamicPathButtons(string TabName)
{
TabPage MyNewTab = new TabPage(TabName);
TabPage TabCopy1;
tabControlEmpresas.TabPages.Add(MyNewTab);
TabControl tc = new TabControl();
tc.Location = new System.Drawing.Point(6, 6);
tc.Size = TabFolders.Size;
for (int i = 0; i < TabFolders.TabCount; i++) {
TabFolders.SelectTab(i);
TabCopy1 = new TabPage(TabFolders.SelectedTab.Text);
foreach (Control c in TabFolders.SelectedTab.Controls) {
Control cNew = (Control)Activator.CreateInstance(c.GetType());
cNew.Text = c.Text;
cNew.Size = c.Size;
cNew.Location = new System.Drawing.Point(c.Location.X, c.Location.Y);
cNew.Visible = true;
if (cNew is TextBox) {
cNew.Name = "txt_" + MyNewTab.Text + "_" + TabFolders.SelectedTab.Text;
}
if (cNew is Button) {
cNew.Name = "btn_" + MyNewTab.Text + "_" + TabFolders.SelectedTab.Text;
cNew.Click += new EventHandler(button_Click);
}
TabCopy1.Controls.Add(cNew);
}
tc.TabPages.Add(TabCopy1);
}
MyNewTab.Controls.Add(tc);
}
After many attempts I did find a very simple solution.
TextBox selectText = new TextBox();
selectText = button.Parent.Controls[TextName] as TextBox;
The button parent hast all the controls.
Assuming that button is the Button control you're creating at run-time you mentioned, you're creating a TextBox control but you're not adding it to the Form.Controls collection (this.Controls.Add([Control])).
Also, you should assign a Location, using a logic that fits your current Layout, to position the newly created Controls. Otherwise, all new controls will be positioned one on top of the other. In the example, the new Control position is determined using a field (int ControlsAdded) that keeps track of the number of Controls created at run-time and add some basic layout logic.
But, if you want to keep a reference of these new Controls, you should add them to a List<Control> or some other collection that allows to select them if/when required.
int ControlsAdded = 0;
protected void button_Click(object sender, EventArgs e)
{
TextBox selectedText = new TextBox();
selectedText.Size = new Size(300, this.Font.Height);
selectedText.Location = new Point(100, ControlsAdded * selectedText.Height + 30);
ControlsAdded += 1;
this.Controls.Add(selectedText);
selectedText.BringToFront();
using (var fBD = new FolderBrowserDialog()) {
if (fBD.ShowDialog() == DialogResult.OK)
selectedText.Text = fBD.SelectedPath;
}
}
with selectText = this.Controls[TextName] as TextBox;, you are trying to find button with replaced name which is not available in this case, and hence it returns null. This is logical mistake.
also string TextName = button.Name.Replace("btn_", "txt_"); does not replace button name, it just assigns replaced string to TextName.
The proper implementation would be
protected void button_Click(object sender, EventArgs e)
{
Button button = sender as Button;
folderBrowserDialog.ShowDialog();
button.Name = button.Name.Replace("btn_", "txt_");
TextBox selectText = new TextBox();
selectText = this.Controls[button.Name] as TextBox;
selectText.Text = folderBrowserDialog.SelectedPath;
}

How do i add a previous and next button to datagridview

I am using Visual studio 2012 and have made a windows form application, for one of the forms I am using a datagridview which shows the information of a table from the SQL database.
I have made the form load information from the datagridview rows directly into a textbox automatically.
SqlDataAdapter SDA = new SqlDataAdapter("SELECT * FROM Stock", con);
DataTable DATA = new DataTable();
SDA.Fill(DATA);
dataGridView1.DataSource = DATA
txtStock3.Text = dataGridView1.SelectedRows[0].Cells[0].Value.ToString();
Descriptioncombo2.Text = dataGridView1.SelectedRows[0].Cells[1].Value.ToString();
txtprice2.Text = dataGridView1.SelectedRows[0].Cells[2].Value.ToString();
The problem is that I need to add a previous button and a next button so that users can navigate through the datagridview rows and see the information in a textbox from each column of a datagridview row. I have looked at similar questions and have browsed through the internet to look for a solution to my problem but i can't seem to find a way which works with my code. Also could you tell me how to add a line of code which tells the user that there is no more rows to select if they click next through all rows of the database.
One way to provide navigation is by using a BindingNavigator where you can remove unnecessary buttons and for TextBox you can data binding.
Code responsible for loading data. Replace the console.writeline in the catch as you see fit e.g. write to a log file etc.
public class DataOperations
{
public DataTable LoadCustomers()
{
DataTable dtCustomers = new DataTable();
using (SqlConnection cn = new SqlConnection(Properties.Settings.Default.ConnectionString))
{
string commandText = #"SELECT [Identfier], [CompanyName],[ContactName],[ContactTitle] FROM [NORTHWND1.MDF].[dbo].[Customers]";
using (SqlCommand cmd = new SqlCommand(commandText, cn))
{
try
{
cn.Open();
dtCustomers.Load(cmd.ExecuteReader());
dtCustomers.Columns["Identfier"].ColumnMapping = MappingType.Hidden;
dtCustomers.Columns["ContactTitle"].ColumnMapping = MappingType.Hidden;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
return dtCustomers;
}
}
On a form, one BindingNavigator, one dataGridView, one TextBox
DataOperations dataOps = new DataOperations();
BindingSource bsCustomers = new BindingSource();
bsCustomers.DataSource = dataOps.LoadCustomers();
dataGridView1.DataSource = bsCustomers;
bindingNavigator1.BindingSource = bsCustomers;
txtContactTitle.DataBindings.Add("Text", bsCustomers, "ContactTitle");
An alternate to the BindingNavigator is to make the BindingSource form level, private variable. Then in buttons call BindingSource.Move method e.g. bsCustomers.MoveFirst(). Of course there is MoveNext, MoveLast and MovePrevious too.
//first
int i = 0;
this.dataGridView1.CurrentCell = dataGridView1.Rows[0].Cells[dataGridView1.CurrentCell.ColumnIndex];
//prev
int prev = dataGridView1.CurrentRow.Index - 1;
if (prev >= 0)
{
this.dataGridView1.CurrentCell = dataGridView1.Rows[prev].Cells[dataGridView1.CurrentCell.ColumnIndex];
//MessageBox.Show(dataGridView1[0, dataGridView1.CurrentRow.Index].Value.ToString());
}
//next
int next = dataGridView1.CurrentRow.Index + 1;
if (next < dataGridView1.Rows.Count)
{
this.dataGridView1.CurrentCell = dataGridView1.Rows[next].Cells[dataGridView1.CurrentCell.ColumnIndex];
//MessageBox.Show(dataGridView1[0, dataGridView1.CurrentRow.Index].Value.ToString());
}
//last
int i = dataGridView1.Rows.Count - 1;
if (i < dataGridView1.Rows.Count)
{
this.dataGridView1.CurrentCell = dataGridView1.Rows[i].Cells[dataGridView1.CurrentCell.ColumnIndex];
//MessageBox.Show(dataGridView1[0, dataGridView1.CurrentRow.Index].Value.ToString());
}
As an alternate to Karen's solution, if you prefer/must go with buttons to navigate then you'll want to handle the CurrentCellChanged event as well as the following button Click events:
private void DataGridView1_CurrentCellChanged(object sender, EventArgs e)
{
if (this.dataGridView1.CurrentRow != null)
{
txtStock3.Text = dataGridView1.CurrentRow.Cells[0].Value.ToString();
Descriptioncombo2.Text = dataGridView1.CurrentRow.Cells[1].Value.ToString();
txtprice2.Text = dataGridView1.CurrentRow.Cells[2].Value.ToString();
this.prevButton.Enabled = this.dataGridView1.CurrentRow.Index > 0;
this.nextButton.Enabled = this.dataGridView1.CurrentRow.Index < this.dataGridView1.Rows.Count - 1;
}
}
private void PrevButton_Click(object sender, EventArgs e)
{
int prev = this.dataGridView1.CurrentRow.Index - 1;
this.dataGridView1.CurrentCell = this.dataGridView1.Rows[prev].Cells[this.dataGridView1.CurrentCell.ColumnIndex];
}
private void NextButton_Click(object sender, EventArgs e)
{
int next = this.dataGridView1.CurrentRow.Index + 1;
this.dataGridView1.CurrentCell = this.dataGridView1.Rows[next].Cells[this.dataGridView1.CurrentCell.ColumnIndex];
}
The CurrentCellChanged event will handle logic for if you can click Previous or Next. Their respective click events simply move the current cell backwards or forwards one row.
You configure the comulmns in the grid to be your matching types. Then after the query you bind the data to this gridView. You add two buttons where the "next" button will fetch the currentselectedrow of the grid, and set it's follower to be the selected one. previous will do the opposite. This is a small pain in the ass. I hate grids in WinForms. The last 4 years, since I did not see them, have been the happiest years of my lif

How to store dynamically created textbox values into sql server database table in WPF

How to store dynamically created textbox values into a SQL Server database table in WPF? I have created textboxes and check boxes dynamically like this,
private void addItembutton_Click(object sender, RoutedEventArgs e)
{
CheckBox chk_add = new CheckBox();
chk_add.MaxWidth = 100;
TextBox tb_add = new TextBox();
tb_add.Width = 200;
tb_add.TextWrapping = TextWrapping.Wrap;
chk_add.Content = tb_add;
wrapPanel1.Children.Add(chk_add);
chk_add.Margin = new Thickness(10, lastheight, 0, 0);
}
Now I want to store these texboxes values into database table and retrieve them into a datagrid?
you need to have reference to those dinamically created textboxes. You may want to store them in a List of TextBox. So that in the end you can iterate trhough the list to get textboxes value, and store them into database. This is the code to clarify what i mean:
List<TextBox> TextBoxes = new List<TextBox>();
private void addItembutton_Click(object sender, RoutedEventArgs e)
{
...
TextBox tb_add = new TextBox();
tb_add.Width = 200;
tb_add.TextWrapping = TextWrapping.Wrap;
TextBoxes.Add(tb_add);
...
}
private void SaveToDatabase()
{
foreach(TextBox tb in TextBoxes)
{
var text = tb.Text;
//save text to database
}
}

how to execute click event on dynamically created button in c#.net

I am trying to build an app, where user can select category and according to it displays its sub categories , these sub categories are buttons, which are dynamically created.
Now, as buttons are dynamically created so I am confuse how to write code under button_click event as I dont know how many subcategories are there.
So is there any way I can execute click event of a particular button , so that I can execute certain commands?
EDITED
This is the code that i tried
Button btnDynamicButton = new Button();
private void btnclick_Click(object sender, EventArgs e)
{
label2.Text = btnDynamicButton.Text;
}
private void btnappetizer_Click(object sender, EventArgs e)
{
groupBox2.Visible =false;
DataTable dt = new DataTable();
dt = itemmasterbl.SelectallrecordFromtblItem(btnappetizer.Text);
for (int i = 0; i < dt.Rows.Count; i++)
{
string name = "Appetizer" + DynamicButtonCount;
Button btnDynamicButton1 = new Button();
btnDynamicButton1.Name = name;
btnDynamicButton1.Text = name;
btnDynamicButton1.Size =
new System.Drawing.Size(150, 30);
btnDynamicButton1.Location =
new System.Drawing.Point(180, DynamicButtonCount * 30);
btnDynamicButton1.Click +=new EventHandler(btnclick_Click);<br>
Controls.Add(btnDynamicButton1);
DynamicButtonCount++;
btnDynamicButton = btnDynamicButton1;
}
}
Once I do this it creates three buttons according to number of values in itemmaster DB under appetizer, but once I click on any of the three buttons the label displays only last buttons text,because in last line I have :
btnDynamicButton = btnDynamicButton1;
Which will last buttons infos,but rather I want which ever button I press, label should display respective text. How can I achieve this.
you can put all your logic into one handler:
System.Windows.Forms.Button b = new System.Windows.Forms.Button();
b.Click += new EventHandler(b_Click);
//finally insert the button where it needs to be inserted.
...
void b_Click(object sender, EventArgs e)
{
MessageBox.Show(((System.Windows.Forms.Button)sender).Name + " clicked");
}
To your edit:
You are storing the reference for your button(s) inside the Field btnDynamicButton. Hence it always gets overwritten with the latest button you have created. You should not reference the button by using a field. The sender parameter of the click-handler contains the button element that has been clicked. See the code above: Simple cast sender to Button and you know which button has been clicked:
private void btnclick_Click(object sender, EventArgs e)
{
Button btn = (Button)sender
label2.Text = btn.Text;
}

change controls based on database value

Problem:
I have a value in a database table. This value can either contain a number, or null. If its null I would like to show one group of controls. If its not null I would like to show another group of controls.
Previous Attempts:
I have tried creating the controls in the code behind depending on the value of the database. This worked. However, on postback I get a null reference exception. The control doesn't exist on postback because the page is stateless. I'm building the controls in the page_load handler (depending on the value of the table column). Since I'm creating the controls in the page_load shouldn't they exist on postback?
I also tried recreating the controls in the event handler for the button. I get a "theres already a control with this id" exception (presumably because I already created it in the page_load method).
I read a few posts about how I have to store the controls in a session. This seems like more work than it should be.
Questions:
Am I going about this the wrong way? This seems like it should have been simple but is turning into a mess.
If this is the correct way to do this, Where do I add the session information? I've been reading other posts and I'm kind of lost
Code:
int bookId;
string empName;
protected void Page_Load(object sender, EventArgs e)
{
if(int.TryParse(Request.QueryString["id"], out bookId))
{
//This is where the value in the database comes into play. If its null Book.GetCopyOwner
// returns a string with length 0
empName = Book.GetCopyOwner(bookId, Request.QueryString["owner"]);
if (empName.Trim().Length > 0)
{
CreateReturnControls();
}
else
{
CreateCheckoutControls();
}
}
}
protected void ReturnButton_Click(object sender, EventArgs e)
{
}
protected void CheckOut_Click(object sender, EventArgs e)
{
int bookId;
if (int.TryParse(Request.QueryString["id"], out bookId))
{
TextBox userId = (TextBox)this.Page.FindControl("UserId");
//WHEN I TRY TO USE THE TEXTBOX userId HERE, I GET NULL REFERENCE EXCEPTION
BookCopyStatusNode.Controls.Clear();
CreateReturnControls();
}
}
protected void CopyUpdate_Click(object sender, EventArgs e)
{
}
private void CreateCheckoutControls()
{
TextBox userId = new TextBox();
//userId.Text = "Enter Employee Number";
//userId.Attributes.Add("onclick", "this.value=''; this.onclick=null");
userId.ID = "UserId";
Button checkOut = new Button();
checkOut.Text = "Check Out";
checkOut.Click += new EventHandler(CheckOut_Click);
TableCell firstCell = new TableCell();
firstCell.Controls.Add(userId);
TableCell secondCell = new TableCell();
secondCell.Controls.Add(checkOut);
BookCopyStatusNode.Controls.Add(firstCell);
BookCopyStatusNode.Controls.Add(secondCell);
}
private void CreateReturnControls()
{
Label userMessage = new Label();
userMessage.Text = empName + " has this book checked out.";
Button returnButton = new Button();
returnButton.Text = "Return it";
returnButton.Click += new EventHandler(ReturnButton_Click);
TableCell firstCell = new TableCell();
firstCell.Controls.Add(userMessage);
TableCell secondCell = new TableCell();
secondCell.Controls.Add(returnButton);
BookCopyStatusNode.Controls.Add(firstCell);
BookCopyStatusNode.Controls.Add(secondCell);
}
It looks like you're creating a static set of controls based on the database value. Why not simply have 2 Panels that contain the controls you want and simply set their visibility to true or false:
if (!Page.IsPostBack)
{
if (int.TryParse(Request.QueryString["id"], out bookId))
{
empName = Book.GetCopyOwner(bookId, Request.QueryString["owner"]);
var display = (empName.Trim().Length > 0);
panelReturnControls.Visible = display;
panelCheckoutControls.Visible = !display;
}
}

Categories

Resources