Why can't asp.net dynamic button fire in loop? - c#

So I found this very puzzling problem which involves dynamic buttons.
Here is my method that creates the buttons:
private void CreateButtons()
{
//Button outside loop works
Button selectItem = new Button();
selectItem.Text = "Hello World";
selectItem.ID = "btn";
selectItem.Click += selectItem_Click;
PlaceHolder1.Controls.Add(selectItem);
int ItemCounter = 0;
for (int i = 0; i < BillDate.Count; i++)
{ //Button inside loop doesnt work
ItemCounter++;
Button selectItem = new Button();
selectItem.Text = "Hello World";
selectItem.ID = "btn-" + ItemCounter.ToString();
selectItem.Click += selectItem_Click;
PlaceHolder1.Controls.Add(selectItem);
}
}
Now here is the problem,
the button that is created outside the loop works fine (event handler selectItem_Click only redirects page).
Why does the button not work inside the loop and why does it work outside the loop?

All of the buttons in your loop have the same ID, since you're not incrementing ItemCounter. While you could just use i instead, you don't appear to be using the ID at all, so you're better off just not setting it in the first place.
Also keep in mind that on the post back the buttons need to be created and added to the page in the PreInit event in order for the event handler to be able to run.
Dynamically creating controls, particularly controls that have handlers on subsequent postbacks, can be quite tricky. It's not uncommon at all to need data from the request to be able to generate the controls, but to need the controls to be generated before the request is processed by ASP for the events to fire. It's dramatically easier to create a template that you bind your data to, using something like a GridView or a Repeater instead, as it will be able to properly handle re-creating the controls before the request is processed while still allowing you to have a dynamic number of instances of the template.

**Use the below code it will work.**
private void CreateButtons()
{
//Button outside loop works
Button selectItem = new Button();
selectItem.Text = "Hello World";
selectItem.ID = "btn";
selectItem.Click += selectItem_Click;
PlaceHolder1.Controls.Add(selectItem);
int ItemCounter = 0;
for (int i = 0; i < BillDate.Count; i++)
{ //Button inside loop doesnt work
Button selectItem = new Button();
selectItem.Text = "Hello World";
selectItem.ID = "btn-" + ItemCounter.ToString();
selectItem.Click += selectItem_Click;
ItemCounter++;
PlaceHolder1.Controls.Add(selectItem);
}
}

Related

sap business one c# development, form and button events

I am creating an addon and for that i have successfully connected di and ui api. i am creating everything (forms, buttons, textbox etc ) manually by code to learn as this is my first one. when i debug i can see my form with all the fields i created. here is the code for form creation.
SAPbouiCOM.FormCreationParams oCreationParams = null;
oCreationParams = ((SAPbouiCOM.FormCreationParams(SBO_Application.CreateObject(SAPbouiCOM.BoCreatableObjectType.cot_FormCreationParams)));
oCreationParams.BorderStyle = SAPbouiCOM.BoFormBorderStyle.fbs_Fixed;
oCreationParams.UniqueID = "Form2";
oForm = SBO_Application.Forms.AddEx(oCreationParams);
oForm.Title = "Simple Form";
oForm.Left = 417;
oForm.Top = 520;
oForm.ClientHeight = 610;
oForm.ClientWidth = 770;
here is how i create my button
SAPbouiCOM.Button oButton = null;
oItem = oForm.Items.Add("Button1", SAPbouiCOM.BoFormItemTypes.it_BUTTON);
oItem.Left = 6;
oItem.Width = 65;
oItem.Top = 51;
oItem.Height = 19;
oItem.Enabled = true;
oButton = ((SAPbouiCOM.Button)(oItem.Specific));
oButton.Caption = "Add";
the problem is when i try to add the values of textbox in database on button click event, i am not able to generate a button click event.
from my knowledge when we create a button from toolbox and uses system form, it automatically initializes the button ON InitializeComponent() function and also creates a delegates pointing to button click event.
May i know how to achieve all these through code.
i tried to initialize button through my manual code and also created delegates pointing to a button click function but i was unable to achieve my result.
Try adding a method that captures SAP B1 item events like this:
public void HandleItemEvent(ref SAPbouiCOM.ItemEvent pVal)
{
if (pVal.BeforeAction == false && pVal.EventType == SAPbouiCOM.BoEventTypes.et_ITEM_PRESSED && pVal.ItemUID == "Button1")
{
// You code here
}
}

C# Dynamically added button click is throwing argument out of bounds exception

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;
}

Get the Text of dynamically created button in C#?

I am dynamically creating buttons in C# with this logic
for (int i = 1; i <= vap; ++i)
{
newButtons[i] = new Button();
newButtons[i].BackColor = Color.Gray;
newButtons[i].Name = "Button4" + i.ToString();
newButtons[i].Click += new EventHandler(NewButtons_Click);
newButtons[i].Location = new System.Drawing.Point(width,height);
newButtons[i].Size = new System.Drawing.Size(76, 38);
tabPage5.Controls.Add(newButtons[i]);
}
This is creating a button and the click event is also working but my problem is I don't know how to get the text of the newly created button. On form load I am putting the text of button from database and this also happening correctly, but I want to know how to get the text of dynamically created buttons.
You won't be able to get the text until after you populate it from the database (careful not to try and get the text too early).
But this should work:
string buttonText = FindControl("Button41").Text;
Update
Since you want the button text from within the click event, you can access the sender object:
Button button = sender as Button;
string buttonText = button.Text;
You just have to set the Text property of the button when you add it.
Using something along the lines of...
string BtnTxt = FindControl("ExampleButton1").Text;
should work fine.
This may cause problems later on however if you are trying to pull text content of buttons in a random order.

Pass a parameter to the ApplicationBarIconButton.Click

*Hi everyone,
I'm new in WP7 dev. (i'm used to work on android) and there is a basic thing i don't know how to do.
I create programmatically a list of ApplicationBarIconButton with this:
for (int i=0; i<menus.Count(); i++)
{
ApplicationBarIconButton button = new ApplicationBarIconButton();
button.IconUri = new Uri(menus.ElementAt(i).Element("ImageUrl").Value.Trim(),UriKind.Relative);
button.Text = menus.ElementAt(i).Element("Title").Value.Trim();
button.Click += new EventHandler(button_clicked);
ApplicationBar.Buttons.Add(button);
}
and I want that the button_clicked method could retrieve the i value of the button.
How is it possible?
Thanks
I was beaten to it by #Enigmativity but his answer may still be incorrect. In my experience I've found that you need to clone the iterating i variable, otherwise on the click event, i will be the last value. If his doesn't work try this (again using a lamba function)
for (int i=0; i<menus.Count(); i++){
ApplicationBarIconButton button = new ...
...
var cloned = i;
button.Click += (sender, e) => {
sometTextBlock.Text = String.Format("App Button {0} pressed.", cloned);
};
}
Cheers,
Al.
You could do this:
for (int i=0; i<menus.Count(); i++)
{
ApplicationBarIconButton button = new ApplicationBarIconButton();
button.IconUri = new Uri(menus.ElementAt(i).Element("ImageUrl").Value.Trim(),UriKind.Relative);
button.Text = menus.ElementAt(i).Element("Title").Value.Trim();
var i2 = i; //Thanks to `ajmccall` - I forgot this.
button.Click += (s, e) =>
{
// the variable `i2` is accessible now.
};
ApplicationBar.Buttons.Add(button);
}
Rather than calling a method to handle click you can use a lambda and still get access to i (via local copy i2). You could then call any method passing i2 as a parameter if you need to.
An integrated way of achieving this is through the use of commanding in MVVM frameworks.
Granted with Application bar buttons / menu items it is a bit more tricky but far more flexible than to manipulate the UI Elements on the page.
Look in to MVVM light (http://mvvmlight.codeplex.com) or further with the likes of Calburn.Micro (http://caliburnmicro.codeplex.com/)
For application bar data binding you will need to google further (lost the link at the mo)

Can't add programmatic controls in ASP.NET

I've got a page in ASP.NET, and I'm dynamically adding a subclass of WebControls.Button to the Controls data member of a pre-existing static TableCell. The button displays fine in the browser as expected. But when I click the button, the event handler I added for button.Click is not being called. Any suggestions as to why this is?
var controls = this.displaytable.Rows[i].Cells[j].Controls;
var button = new TableButton(j, i);
button.Click += new EventHandler(this.button_Click);
button.UseSubmitBehavior = false;
button.Text = "Available";
controls.Add(button);
Dynamically added buttons must be created on every request, most likely it is sufficient before raising postback events (e.g. OnLoad). Button needs to have an explicit ID sometime:
var controls = this.displaytable.Rows[i].Cells[j].Controls;
var button = new TableButton(j, i);
button.Click += new EventHandler(this.button_Click);
button.UseSubmitBehavior = false;
button.Text = "Available";
button.Id = string.Format("TableButton_{0}_{1}", j, i);
controls.Add(button);
This SO answer may help little bit: ASP.NET dynamic Command Button event not firing.

Categories