Please help me understand what is this error that I'm getting:
lblTabCounter is a label coded in the aspx page while the lblc[index] is a collection of label created at runtime during page load.
Declaration outside of page load:
Label[] lblc = new Label[10];
Inside Page Load Event:
for (int i = 0; i < 10; i++)
{
lblc[i] = new Label() { Text = (i + 1).ToString() };
this.Controls.Add(lblc[i]);
}
Inside another event called NodeChanged:
int TabCount = Convert.ToInt32(lblTabCounter.Text.ToString());
int TabIndex = Convert.ToInt32(lblTabCounterIndex.Text.ToString());
if(TabCount <= 10)
{
divcont.Visible = true;
string tabName = getURLName(uRL);
MenuItem myItem = new MenuItem(tabName, TabIndex.ToString());
Menu1.Items.AddAt(TabIndex, myItem);
//f1.Attributes["src"] = url;
f1.Attributes.Add("src", lblURL.Text.ToString());
MultiView1.ActiveViewIndex = TabIndex;
lblc[TabCount].Text = lblTabCounter.Text;
lblc[TabCount + 1].Text = lblURL.Text;
TabCount++;
TabIndex++;
lblTabCounter.Text = TabCount.ToString();
lblTabCounterIndex.Text = TabIndex.ToString();
tvPermissions.ExpandAll();
//tvPermissions.CollapseAll();
int i = ctr;
}
Note: This are all inside site.master.
The problem is your web page is refreshing and losing the state of the labels.
Label[] lblc = new Label[10];
protected void Page_Load(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
lblc[i] = new Label();
this.Controls.Add(lblc[i]);
if (Session["lblc" + i.ToString()] == null)
Session["lblc" + i.ToString()] = lblc[i].Text = (i + 1).ToString();
else
lblc[i].Text = (string)Session["lblc" + i.ToString()];
}
Then when you want to set a label you use the following (when the page is not being refreshed by the event)
lblc[4].Text = "cool";
Session["lblc4"] = "cool";
However because your click event is refreshing the page it loses contact with the lblc so you only set the Session so upon refresh you will see your new Label. (when the page is being refreshed by the event)
Session["lblc4"] = "cool";
The page is in the process of refreshing as a result of your particular event so the label disappears but the session state remains so when you set the session upon refresh the code grabs the session instead of setting it to the default number.
Rather than change the text of the label when it refreshes you are actually generating the new label with the session string you set.
Also make sure you have <sessionState mode="InProc" /> in your Web.config file under <system.web>
Please read more on Session States here http://msdn.microsoft.com/en-us/library/87069683(v=vs.80).aspx
There are two possible issues with that line of code:
lblc[TabCount] is null.
lblTabCount is null.
Since you are paused in the debugger, you can see which of those is the case, then look around in the rest of the code to find out why.
I would follow the path of the lblc[index] array to determine if the element offset is within range as well as it being created properly and not ending up there as a null (whether the null being the object lblc[index] or the text property) being referenced.
Related
I am creating asp.net web form. in that i am creating dynamic tables in which particular column is numeric text box control.
i don't know how to assign and get values from the text box control.. my coding as follow..
for (int i = 0; i < my_DataTable.Rows.Count; i++)
{
HtmlTableRow _Row = new HtmlTableRow();
HtmlTableCell Col = new HtmlTableCell();
Col.InnerText = my_DataTable.Rows[i]["itmCode"].ToString();
_Row.Controls.Add(Col);
Col = new HtmlTableCell();
_Row.Controls.Add(Col);
Col.InnerHtml = "<input type='number' value='0'>";
_Row.Controls.Add(Col);
my_Table.Rows.Add(_Row);
}
In a paricular method, i need to assign the value to the text box control also needs to get the value existing value.. so i try follow as below
var no_1 = my_Table.Rows[0].Cells[1].InnerText;
If i check the no_1, it has the textbox, but i don't know how to access the current value and assign new value..
can anyone help me how to achieve this..
One thing you have to keep in mind while working with Dynamic Controls is that whenever a postback has occurred you will lose the dynamically created controls(as the postback calls the Page_load() event so if you don't have them at the load event they will not be generated and hence will not be displayed.). So, it is always better to re-render the controls in the load event.
So, in order to get the value of the dynamically assigned controls (either HTML or Asp.net) here is how i would do that.
First, create a holder which will be used to store the controls in the page either with runat="server"(So, you can access that control in the backend). In your case, that control is my_Table. Then use the Session/ViewState to keep a track of all the created dynamic controls which can be used re-render the controls with their values as:
To add a new control in the page it would be like this:
var cnt = _findRelated("txtDynamic") + 1; //for all the dynamic text boxes i am using the prefix of txtDynamic just to keep SOC.
var nId = $"txtDynamic-{cnt}";
var _ctrl = new HtmlInputText("Integer")
{
Name = nId,
ID = nId,
//Value="Default Value" //uncomment to assign a default value
};
_ctrl.Attributes.Add("runat", "server");
var row = new System.Web.UI.HtmlControls.HtmlTableRow();
var newCell = new HtmlTableCell();
newCell.Controls.Add(_ctrl);
row.Cells.Add(newCell);
my_Table.Rows.Add(row);
Session.Add(cnt.ToString(), _ctrl); //here i am using session to manage the controls but you can also use the ViewState
In the above code i am using HtmlInputText to generate an <input type="number"></input> with it's constructor taking the type string more can be read at:HtmlInputText.
The _findRelated() method is used to get the number of dynamic text controls appended to the Form. It is defined as:
private int _findRelated(string prefix)
{
string reqstr = Request.Form.ToString();
return ((reqstr.Length - reqstr.Replace(prefix, "").Length) / prefix.Length);
}
To set the value of the dynamically added control we can do something like this(if not assigned at the creation):
var cell = my_Table.Rows[_myTable.Rows.Count-1].cells[0]; //here i have assumed it is in the last row and in the first cell you can change the index to be anything.
var txtDynamic = cell.Controls.OfType<HtmlInputText>().FirstOrDefault();//getting the control
txtDynamic.Value = "<Some thing new>"; //setting the value
Now, to get the assigned the value:
var cell = my_Table.Rows[_myTable.Rows.Count-1].cells[0]; //here i have assumed it is in the last row and in the first cell you can change the index to be anything.
var txtDynamic = cell.Controls.OfType<HtmlInputText>().FirstOrDefault();//getting the control
//now use the .Value property of the control to get the value as:
var nValue = txtDynamic.Value;
And as we know the dynamically added controls will be lost on the postback event then we can create a method which will use the controls stored in the Session and re-render them with their values as:
private void _renderControls()
{
try
{
if (Session.Count > 0)
{
for (int k = 0; k < Session.Count; k++)
{
if (Session[k] != null)
{
var _ctrl = new HtmlInputText("Integer") //you can make it dynamic to add different types of input control
{
Name = ((HtmlInputText)Session[k]).ID,
ID = ((HtmlInputText)Session[k]).ID,
Value = ((HtmlInputText)Session[k]).Value
};
if (_ctrl != null)
{
_ctrl.Attributes.Add("runat", "server");
var row = new System.Web.UI.HtmlControls.HtmlTableRow();
var newCell = new HtmlTableCell();
newCell.Controls.Add(_ctrl);
row.Cells.Add(newCell);
my_Table.Rows.Add(row);
}
}
}
}
}
catch (Exception ex)
{
throw ex;
}
}
Now, let's modify the Page_load() event to call this method on every postback as:
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
_renderDynamic(); // this method will be called if there is an postback event to re-render the dynamic controls
}
}
Note:
This is just a sample(there can be a lot better approaches out there).
I have used HtmlInputText with property as Integer to create ainput[type="number"].
QUESTION UPDATE
I am using this piece of code for cor adding multiple labels on button click.
But each and every time it gives count value 0 and after execution only same label comes.
int count = (int)ViewState["count"];
for (int i = 0; i <= count; i++)
{
TextBox txtnew = new TextBox();
txtnew.ID = "label_" + (i + 1);
txtnew.Text = "label_" + (i + 1);
ViewState["count"] = i;
pnlControl.Controls.Add(txtnew);
}
ViewState["count"] = count + 1;
What i want now is how to keep that data of each control binded to it in its more convenient way.
Dynamic controls are lost on every PostBack, so you need to keep track of the created ones somewhere and recreate them when the page is reloaded.
See my anwer here: How to dynamically create ASP.net controls within dynamically created ASP.net controls
It is about buttons but the basic principle is the same. Just replace Button with Label and it will work.
That's cause it's a web application and thus the session is not retained cause web applications are stateless in nature. So, every post back you get a new page instance which will have int count is 0. Solution: Use Session to store the data for future re-use like
int count = pnlControl.Controls.OfType<Label>().ToList().Count;
Session["count"] = count;
Retrieve it back on postback request
if(Session["count"] != null)
count = (int)Session["count"];
newbie programmer here after hours of searching has left me stumped.
I'm having trouble with referencing a control inside a tab created at RunTime with a button press. Basically what I have is a tabletop RPG calculator, using a Windows Form, that has a tabControl holding tab pages, with each tab page holding user-inputted stats for that individual enemy to be used in calculations.
The problem is that I want the user to be able to click a button to generate a new enemy tab page. Here is my code for generating an enemy tab page with a TextBox.
int enemyNumber = 0;
// Creates a new Enemy Tab
private void button2_Click_1(object sender, EventArgs e)
{
// Create a new TabPage
var newTabPage = new TabPage()
{
Text = "Enemy " + enemyNumber,
};
// Add Enemy Name Box
var newEnemyNameBox = new TextBox()
{
Name = "enemyNameBox" + enemyNumber,
Text = "",
Location = new Point(127, 11),
Size = new Size(133, 20)
};
// Add the controls to the new Enemy tab
newTabPage.Controls.Add(newEnemyNameBox);
// Add the TabPage to the TabControl
tabControl1.TabPages.Add(newTabPage);
// Increases the enemy's "reference number" by 1
// So that enemy tabs will be generated in order enemyTab0, enemyTab1, etc.
enemyNumber += 1;
}
This all works nicely. Unfortunately, after this point things have gotten ugly. I need to reference that TextBox named "enemyNameBox" + enemyNumber, and I'm not sure how to do so.
What I did was create "archVariables" to store the values from whatever enemy tab is selected, then use the appropriate archVariable in the program's calculations. IE: archEnemyName. The idea is that whatever tab the user is currently selected on (determined via SelectedIndex) the TextBox from that page will be used for the program's output.
Here are the two things I've tried after researching the matter:
// Attempt 1
private void defendCalcButton_Click(object sender, EventArgs e)
{
for (int i = 0; i < tabControl1.SelectedIndex; i++)
{
archEnemyNameBox = ((TextBox)Controls["enemyNameBox" + i]).Text;
}
}
This code simply throws a NullReferenceException when I press the button. So after researching more I tried this:
// Attempt 2
private void defendCalcButton_Click(object sender, EventArgs e)
{
for (int i = 0; i < tabControl1.SelectedIndex; i++)
{
TextBox tb2 = new TextBox();
tb2 = ((TextBox)(enemyTab.Controls.Find("enemyNameBox" + i, true)));
archEnemyNameBox = tb2.Text;
}
}
This time I got an Error: Cannot convert type 'System.Windows.Forms.Control[]' to 'System.Windows.Forms.TextBox'
I feel like the second method I have here is probably closer to the correct way to do this, but apparently I'm still not getting it right. I've learned a lot by searching the information on stackoverflow and msdn.microsoft but nothing has gotten me past this problem.
Any help would be appreciated.
basically the problem with your second attemp is that enemyTab.Controls.Find("enemyNameBox" + i, true) returns an array of Controls Control[] and you're trying to convert that to a Control here is the problem, you should get the first control in that array and then convert it to a Control so it should be like this:
private void defendCalcButton_Click(object sender, EventArgs e)
{
for (int i = 0; i < tabControl1.SelectedIndex; i++)
{
TextBox tb2 = new TextBox();
tb2 = ((TextBox)(enemyTab.Controls.Find("enemyNameBox" + i, true)[0]));
archEnemyNameBox = tb2.Text;
}
}
but it is not the BestWay to do so it seems that everytime a user adds a new tabPage it will have the same Controls right? so why not create an userControl with any Control you have on your TabPage? so when you press the user press to add a new tab your code should be like so:
private void CreateNewEnemyTab()
{
var newTabPage = new TabPage()
{
Text = "Enemy " + enemyNumber,
};
EnemyTabUserControl enemyTab = new EnemyTabUserControl(enemyNumber);
here the EnemyTabUserControl should have all the components you need;
newTabPage.Controls.Add(enemyTab);
tabControl1.TabPages.Add(newTabPage);
}
and the code to bring the TextBox from the current tab could be as follow (you are going to need to reference LINQ)
using System.Linq;
//First Lets create this property, it should return the selected EnemyTabUserControl inside the tabControl
public EnemyTabUserControl CurrentTab {
get {
return tabControl1.SelectedTab.Controls.OfType<EnemyTabUserControl>().First();
}
}
// then if we make the textbox you want to reference from outside the code we can do this
CurrentTab.NameOfTheTextBox;
Patrick has solved your fundamental problem, but I don't think you need the loop in there at all. Here I've broken the steps out so you can see what needs to happen a little better:
private void defendCalcButton_Click(object sender, EventArgs e)
{
Control[] matches = this.Controls.Find("enemyNameBox" + tabControl1.SelectedIndex.ToString(), true);
if (matches.Length > 0 && matches[0] is TextBox)
{
TextBox tb = (TextBox)matches[0];
archEnemyNameBox = tb.Text;
}
}
Basically I am trying to create an attachment window utilizing keeping everything in lists for easy use later. So, every time the form loads it goes through everything in the list and creates both labels and buttons for them. There is no errors until I click my button. If I click any of the X buttons, I get an argument out of bounds exception on the click += line. What's interesting is why its being called? The click isn't supposed to add another event handler to itself. Its also interesting that on click the indicie is one greater than the total count so how its even executing that line is beside me considering it should never iterate higher that its max count. Any help would be greatly appreciated.
ArrayList attachmentFiles;
ArrayList attachmentNames;
public Attachments(ArrayList attachments, ArrayList attachmentFileNames)
{
InitializeComponent();
attachmentFiles = attachments;
attachmentNames = attachmentFileNames;
}
private void Attachments_Load(object sender, EventArgs e)
{
ScrollBar vScrollBar1 = new VScrollBar();
ScrollBar hScrollBar1 = new HScrollBar();
vScrollBar1.Dock = DockStyle.Right;
hScrollBar1.Dock = DockStyle.Bottom;
vScrollBar1.Scroll += (sender2, e2) => { pnl_Attachments.VerticalScroll.Value = vScrollBar1.Value; };
hScrollBar1.Scroll += (sender3, e4) => { pnl_Attachments.HorizontalScroll.Value = hScrollBar1.Value; };
pnl_Attachments.Controls.Add(hScrollBar1);
pnl_Attachments.Controls.Add(vScrollBar1);
Label fileName;
for (int i = 0; i < attachmentNames.Count; i++)
{
fileName = new Label();
fileName.AutoSize = true;
fileName.Text = attachmentNames[i].ToString();
fileName.Top = (i + 1) * 22;
pnl_Attachments.Controls.Add(fileName);
Button btn_RemoveAttachment = new Button();
btn_RemoveAttachment.Text = "X";
btn_RemoveAttachment.Tag = i;
btn_RemoveAttachment.Click += new System.EventHandler((s, e3) => removeAttachment(s, e3, attachmentFiles[i].ToString(), attachmentNames[i].ToString()));
btn_RemoveAttachment.Top = (i + 1) * 22;
btn_RemoveAttachment.Left = fileName.Right + 22;
pnl_Attachments.Controls.Add(btn_RemoveAttachment);
}
}
private void removeAttachment(object sender, EventArgs e, string file, string fileName)
{
attachmentNames.Remove(fileName);
attachmentFiles.Remove(file);
pnl_Attachments.Controls.Clear();
this.Close();
}
In my test, attachmentFiles had a count of 3 and attachmentNames had a count of 3. On form load, there are no issues. But, on button click I get an exception because somehow its trying to add another click listener to a button with i = 3 (a.k.a a 4th element)
The problem is not with the event subscription, but with the event handler execution.
You are running into this problem because a closure is created for the event handler, but the value i is modified by the for loop. The last value of i will be 1 + attachmentNames.Count and this will be the value used by each invocation of the event handler.
For more detail as to why this happens you can check out the question and answer here: Access to Modified Closure.
To resolve the problem, you can assign i to another variable:
var currentAttachmentIndex = i;
btn_RemoveAttachment.Click += new System.EventHandler((s, e3) => {
removeAttachment(s,
e3,
attachmentFiles[currentAttachmentIndex].ToString(),
attachmentNames[currentAttachmentIndex].ToString())
});
Or you can use the value you already captured in the Tag property of the btn_RemoveAttachment control.
btn_RemoveAttachment.Click += new System.EventHandler((s, e3) => {
var senderButton = (Button)s;
var currentAttachmentIndex = (int)senderButton.Tag;
removeAttachment(s,
e3,
attachmentFiles[currentAttachmentIndex].ToString(),
attachmentNames[currentAttachmentIndex].ToString())
});
Keep in mind, though, if you are removing items from the List, the indexes will not be valid. Understanding how closures work, however, should help you solve that problem if it arises (it looks like you close the form anyway after the first removal).
Presumably, the attachmentFiles[i] is what is causing the out of bounds exception, perhaps attachmentFiles has fewer elements than attachmentNames?
Why not set a breakpoint on that line and check what is causing the out of bounds exception?
I get an argument out of bounds exception on the click += line. What's interesting is why its being called? The click isn't supposed to add another event handler to itself
It looks like the exception is not thrown at the event subscription (+=) but at the lambda function declared in that same line
Its also interesting that on click the indicie is one greater than the total count so how its even executing that line is beside me considering it should never iterate higher that its max count. Any help would be greatly appreciated
The value of i is fixed when you assign the lambda to the event. The indexes at the attachmentFiles change as you remove elements, but the indexes used by the lambda to access it don't. Let's try an example.
let's assume we have an array with 4 attchements (index:attachment))
[0:att0, 1:att1, 2:att2, 3:att3]
And 4 buttons that execute this lambdas
[removeAt(0), removeAt(1), removeAt(2), removeAt(3)]
We click the second button and it correctly removes the second attachment from array, now we have:
[0:att0, 1:att2, 2:att3]
[removeAt(0), removeAt(1), removeAt(2), removeAt(3)]
Now we click the fourth button. It tries to remove the attachment with index 3 and the out of bounds exception is thrown because that index doesn't exist anymore (and even if it existed, it might not point to the right attachment!)
Another approach would be to modify your 'removeAttachment' method, and use that as your event handler for all buttons.
An example of this would be:
for (int i = 0; i < attachmentNames.Count; i++)
{
var lbl_FileName = new Label
{
AutoSize = true,
Name = "Label" + i, // Give this a name so we can find it later
Text = attachmentNames[i],
Top = (i + 1) * 22
};
var btn_RemoveAttachment = new Button
{
Text = "X",
Tag = i,
Top = (i + 1) * 22,
Left = lbl_FileName.Right + 22
};
btn_RemoveAttachment.Click += removeAttachment;
pnl_Attachments.Controls.Add(lbl_FileName);
pnl_Attachments.Controls.Add(btn_RemoveAttachment);
}
Then you can modify your removeAttachment method to be an EventHandler, and to detect the button and associated label using the sender As Button and Button.Tag property:
private void removeAttachment(object sender, EventArgs e)
{
// Get associated Label and Button controls
var thisButton = sender as Button;
var index = Convert.ToInt32(thisButton.Tag);
var thisLabel = (Label) Controls.Find("NameLabel" + index, true).First();
// Remove the files
int itemIndex = attachmentNames.IndexOf(thisLabel.Text);
attachmentNames.RemoveAt(itemIndex);
attachmentFiles.RemoveAt(itemIndex);
// Disable controls and strikethrough the text
thisButton.Enabled = false;
thisLabel.Font = new Font(thisLabel.Font, FontStyle.Strikeout);
thisLabel.Enabled = false;
}
I am trying to get an asp.net chart and it's legend to allow me to open up another page in another tab passing the values of the piece of the chart I clicked on with it. I have been able to get it to open up another tab when clicking on the chart by doing the following but it does not pass the data.
Chart2.Series[0].LegendUrl = "chartdetails.aspx";
Chart2.Series[0].Url = "chartdetails.aspx";
Chart2.Series[0].LegendMapAreaAttributes="target=\"_blank\"";
Chart2.Series[0].LegendPostBackValue = "#VALY-#VALX";
Chart2.Series[0].MapAreaAttributes="target=\"_blank\"";
Chart2.Series[0].PostBackValue = "#VALY-#VALX";
If I leave out the urls and mapareaattributes I can then get it to go to the onclick where I am able to get the data, put it in a session variable and use Reponse.Redirect to open the new page where it sees the session variable data,however it doesn't open in another tab, it opens in the same tab.
Chart2.Series[0].LegendPostBackValue = "#VALY-#VALX";
Chart2.Series[0].PostBackValue = "#VALY-#VALX";
protected void Chart2_Click(object sender, ImageMapEventArgs e)
{
HttpContext.Current.Session["VAL"] = e.PostBackValue;
Response.Redirect("chartdetails.aspx", false);
}
How can I get it to do both? Does Response.Redirect have a way to open a new tab? Some research leads me to believe it does not. Is there a way to get both the server side onclick event to run, so I can set the session variable and the chart.series.url to fire after the server side click runs so the session variable would be set before I open the new tab?
I'm feeling like this may be a case of "I can't have my cake and eat it too."
As it turns out I can have my cake and eat it too. If I set the url, postbackvalues, and legendmapareaattributes in my Page_Load and set up the click for the chart to put the PostBackValue in the session variable when you click on the chart it saves the value in the session variable that is listed in the PostBackValue of the Series of the chart. It then opens in a new tab chartdetails.aspx where I can access the information from the session variable.
Chart2.Series[0].LegendUrl = "chartdetails.aspx";
Chart2.Series[0].LabelUrl = "chartdetails.aspx";
Chart2.Series[0].Url = "chartdetails.aspx";
Chart2.Series[0].LegendPostBackValue = "#VALY-#VALX";
Chart2.Series[0].LabelPostBackValue = "#VALY-#VALX";
Chart2.Series[0].PostBackValue = "#VALY-#VALX";
Chart2.Series[0].LegendMapAreaAttributes = "target=\"_blank\"";
Chart2.Series[0].LabelMapAreaAttributes = "target=\"_blank\"";
Chart2.Series[0].MapAreaAttributes="target=\"_blank\"";
protected void Chart2_Click(object sender, ImageMapEventArgs e)
{
HttpContext.Current.Session["VAL"] = e.PostBackValue;
}
I can't use postback to get the value on the series for some reason. So, I want to share my way inspired by #Adam that is loop over the series points after data binding the chart and set URL.
GET:
Series s=new Series("Test");
/*
* After Data bind to the series s
*/
for (int p = 0; p < s.Points.Count; p++)
{
s.Points[p].Url ="test.aspx?name1=value1&name2=value2";
s.Points[p].MapAreaAttributes = "target=\"_blank\"";
}
POST:
(I put javascript function in url. So, It will execute the javascript for me to send a form I created in the function to the text.aspx.)
Series s=new Series("Test");
/*
* After Data bind to the series s
*/
for (int p = 0; p < s.Points.Count; p++)
{
s.Points[p].Url ="javascript:(function(){" +
"var mapForm = document.createElement('form');mapForm.target = '_blank';" +
"mapForm.method = 'POST';mapForm.action = 'test.aspx';" +
"var mapInput = document.createElement('input');mapInput.type = 'hidden';" +
"mapInput.name = 'partID';mapInput.value = 'put any value you need';" +
"mapForm.appendChild(mapInput);document.body.appendChild(mapForm);" +
"mapForm.submit();document.body.removeChild(mapForm);})();";
}
Reference:
Javascript pass values using POST