How To Dynamically Add Events to Dynamically Created Buttons - c#

I have a small ASP web application which can be used to determine whether or not 2 users have been assigned the same seat number.
To display the results / functionality, I decided to go with an asp:Table, where each row has 2 buttons (one for each user).
The administrator can click either one of the buttons to clear that user's seat number value from the system.
Here is the code which builds the table cells:
BuildDuplicateTable (called in Page_Load)
private void BuildDuplicateTable(List<Duplicate> duplicates)
{
foreach (var dup in duplicates)
{
var row = new TableRow();
var user1cell = new TableCell();
var seatcell = new TableCell();
var user2cell = new TableCell();
var button1 = new Button();
button1.Text = $"{dup.UserOne.UserName}";
var button1cell = new TableCell();
button1cell.Controls.Add(button1);
button1.Click += new EventHandler(Test);
var button2 = new Button();
button2.Text = $"{dup.UserTwo.UserName}";
var button2cell = new TableCell();
button2cell.Controls.Add(button2);
button2.OnClientClick = "return true";
button2.Click += (sender, eventArgs) =>
{
ActiveDirectory.ClearProperty(dup.UserTwo.UserName, "extensionAttribute2");
};
user1cell.Text = dup.UserOne.UserName;
seatcell.Text = dup.UserOne.SeatNumber;
user2cell.Text = dup.UserTwo.UserName;
row.Cells.Add(button1cell);
row.Cells.Add(seatcell);
row.Cells.Add(button2cell);
MyAspTable.Rows.Add(row);
}
}
My issue is that when I click on any of the buttons, the page is simply refreshed, and the data is no longer displayed (as I am handling postback in Page_Load). My event handler never fires ... Notice that in the code above I left in 2 separate methods of attaching an event handler that I tried - neither of them works!
Duplicate
class Duplicate
{
public UserSeatNumberRelationship UserOne;
public UserSeatNumberRelationship UserTwo;
public Duplicate(UserSeatNumberRelationship userone, UserSeatNumberRelationship usertwo)
{
UserOne = userone;
UserTwo = usertwo;
}
}
UserSeatNumberRelationship
class UserSeatNumberRelationship
{
public string UserName;
public string SeatNumber;
public UserSeatNumberRelationship(string username, string seatnumber)
{
UserName = username;
SeatNumber = seatnumber;
}
}
Page_Load
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack) return;
DuplicateList = FindDuplicates();
BuildDuplicateTable(DuplicateList);
}
Test
protected void Test(object sender, EventArgs e)
{
ActiveDirectory.ClearProperty(UserName, "extensionAttribute2");
}

As ConnersFan mentioned in the comments, this was being caused by my PostBack handler in Page_Load.
So after removing this line
if (Page.IsPostBack) return;
The event handlers work fine.

Related

Checkbox fires more than one event

I have a dynamic tab where I have checkboxes. I put an event onCheckedChanged on them. It works first time, but after postback, my tab is recreated and when I click on another checkbox, I get more than one event being triggered.
Here is the code to create the tab :
private void initCatalog()
{
foreach (Article art in listArticle)
{
TableRow ligne = new TableRow();
ligne.Width = Unit.Percentage(100);
TableCell celluleITMREF = new TableCell();
celluleITMREF.Width = Unit.Percentage(10);
celluleITMREF.Text = art._ITMREF;
TableCell celluleCBOX = new TableCell();
celluleCBOX.Width = Unit.Percentage(8);
CheckBox cbox = new CheckBox();
cbox.ID = "cbox." + f._FOURNISSEUR + "." + art._ITMREF;
cbox.Checked = hfArticlesPaniers.Value.Contains(cbox.ID);
//cbox.Enabled = !(hfArticlesPaniers.Value.Contains(cbox.ID));
cbox.CheckedChanged += new EventHandler(cbox_CheckedChanged);
cbox.AutoPostBack = true;
cbox.CssClass = "c";
celluleCBOX.Controls.Add(cbox);
ligne.Cells.Add(celluleITMREF);
ligne.Cells.Add(celluleCBOX);
tabArticle.Rows.Add(ligne);
}
}
Here is the event for the checkbox :
protected void cbox_CheckedChanged(object sender, EventArgs e)
{
CheckBox c = sender as CheckBox;
Response.Write("<script>alert(\"" + c.ID + "\");</script>");
}
Here is the page_Load event :
protected void page_Load(object sender, EventArgs e){
this.initCatalog();
}
Thanks for your help :)
Each iteration of art in listArticle will cause the event handler to be added to the event by the line
cbox.CheckedChanged += new EventHandler(cbox_CheckedChanged);
executing each time. (i.e. you're adding the handler multiple times)
In addition, each running of initCatalog will also cause this if the page is not destroyed and loaded again in between.
Move the above line to a place where it will only be executed once after the page loads.
EDIT
on re-reading your code, adding multiple times within the loop is probably what you intended as there are multiple checkboxes. But after the postback, the initCatalog() may be executed again, thereby adding the event once more?
I found the mistake. I've a method who order the list after she was create ... I deleted the method and it worked.
I don't know why it made mistakes ...
private void orderCatalog()
{
var tab = from TableRow tr in tabArticle.Rows
orderby tr.Cells[1].Text
select tr;
List<TableRow> l = new List<TableRow>();
foreach (TableRow tr in tab)
{
l.Add(tr);
}
tabArticle.Rows.Clear();
foreach (TableRow tr in l)
{
tabArticle.Rows.Add(tr);
}
}

C# Dynamic button not firing click event

It reloads the page empty when I click the button. How do I fire click event on button click? I think Page.IsPostBack is the reason it reloads the page empty instead of showing the label.
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack == false)
{
account account = new account();
accountManager accountManager = new accountManager();
group group = new group();
groupManager groupManager = new groupManager();
string emailAddress;
emailAddress = HttpContext.Current.User.Identity.Name;
account = accountManager.getAccInfoByEmailAddress(emailAddress);
group = groupManager.getGroupLeader(account.groupNo);
if (account.groupNo == 0)
{
divMessage.InnerHtml = "You are not in any group.";
}
else
{
try
{
Button btn = new Button();
btn.Text = "Click";
btn.Click += new EventHandler(button_Click);
form1.Controls.Add(btn);
}
catch (Exception)
{
divMessage.InnerHtml = "Unable to retrieve data. Please contact administrator if the problem persists.";
}
}
}
}
.
private void button_Click(object sender, EventArgs e)
{
Label Label1 = new Label();
Label1.Text = "rthfg";
form1.Controls.Add(Label1);
}
When you click the button, or somehow else generate a postback, ASP.NET creates the page (as it always does) and tries to find the source of the request, that is the button you clicked. In your case this button is no longer on the page, so ASP.NET cannot find anything, end does not fire the event.
Resolution seems easy enough in your case - just always create the button and put it on the page, regardless of the postback:
if (!Page.IsPostBack)
{
...
}
Button btn = new Button();
btn.Text = "Click";
btn.Click += new EventHandler(button_Click);
form1.Controls.Add(btn);
Btw, why make the button dynamic? Dynamic controls are always harder to manage.

Click event of dynamically created Button not firing

I'm creating dynamically generated buttons, and when I click on the button, the Add_Click method doesn't get fired up.
Here is a sample from my code:
protected void SearchRec(object sender, EventArgs e)
{
SearchResultsPanel.Controls.Clear();
string text_to_search = SearchTB.Text;
Friends RecToSearch = new Friends();
List<Friends> ListNFU = DBS.getNonFriendUsers(User.Identity.Name.ToString(), text_to_search);
if (ListNFU.Count != 0)
{
foreach (Friends NFRIndex in ListNFU)
{
string _FriendsOutput = FR_output(NFRIndex);
HyperLink RecHyperLink = new HyperLink();
RecHyperLink.Text = _FriendsOutput;
RecHyperLink.CssClass = "HyperLinkFriends";
RecHyperLink.ID = NFRIndex.UdName;
SearchResultsPanel.Controls.Add(new LiteralControl("<div style='height:32px'>"));
SearchResultsPanel.Controls.Add(RecHyperLink);
Button addUser = new Button();
addUser.CssClass = "ApproveBTN";
addUser.Text = "send";
addUser.Click += new EventHandler(Add_Click);
addUser.ID = NFRIndex.UdName + "3";
SearchResultsPanel.Controls.Add(addUser);
}
}
else
{
Label NoResultsLabel = new Label();
NoResultsLabel.Text = "Nothing is found";
SearchResultsPanel.Controls.Add(NoResultsLabel);
}
SearchResultsPanel.Controls.Add(new LiteralControl("</div>"));
}
private void Add_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
string _tempID = btn.ID;
string id = _tempID.Substring(0, _tempID.LastIndexOf('3'));
DateTime cdate = new DateTime();
cdate = DateTime.Now;
DBS.AddFriend(User.Identity.Name, id, cdate);
btn.Visible = false;
btn.NamingContainer.FindControl(id).Visible = false;
}
Note: I did something very similar on page_load and it does work.
That is because when the page is reloaded, the control is most probably not recreated. That means that the event won't fire indeed.
You need to place this kind of code in the Page_Load so it gets recreated at postback.

GridView RowCommand Events not firing when button created with an external class

Ok The problem is I have a grid that I need to have paging on it with pages shown as numbers
I want to add two link button to the paging section to alow user to navigate to next page prev page
Here is my code
protected void CustomerGridView_RowCreated(object sender, GridViewRowEventArgs e)
{
var grid = sender as GridView
if (e.Row.RowType == DataControlRowType.Pager)
{
var prvLink = new LinkButton();
prvLink.Text = "<";
prvLink.CommandName = "Page";
prvLink.CommandArgument = "Prev";
prvLink.EnableViewState = true;
var nextLink = new LinkButton();
nextLink.Text = ">";
nextLink.CommandName = "Page";
nextLink.CommandArgument = "Next";
nextLink.EnableViewState = true;
var prvCell = new TableCell();
var nextCell = new TableCell();
prvCell.Controls.Add(prvLink);
nextCell.Controls.Add(nextLink);
Table pagerTable = e.Row.Controls[0].Controls[0] as Table;
TableRow row = pagerTable.Rows[0];
row.Cells.AddAt(0, prvCell);
row.Cells.AddAt(row.Cells.Count, nextCell);
if (grid.PageIndex == 0)
{
prvCell.Enabled = false;
}
if (grid.PageIndex == grid.PageCount - 1)
{
nextCell.Enabled = false;
}
}
}
its perfectly working and users are able to navigate back and forward (and I can see grid RowCommand event getting fired)
The problem is I do not want to put the code inside my page (to make my page tiny and put the responsibility to an other class )
here is my class
public class GridStyler
{
private GridView _grid;
public GridStyler(GridView grid)
{
_grid = grid;
}
public void AddNextPreviousOnPager()
{
_grid.RowCreated += _grid_RowCreated;
}
void _grid_RowCreated(object sender, GridViewRowEventArgs e)
{
var grid = sender as GridView;
if (e.Row.RowType == DataControlRowType.Pager)
{
var prvLink = new LinkButton();
prvLink.Text = "<";
prvLink.CommandName = "Page";
prvLink.CommandArgument = "Prev";
prvLink.EnableViewState = true;
var nextLink = new LinkButton();
nextLink.Text = ">";
nextLink.CommandName = "Page";
nextLink.CommandArgument = "Next";
nextLink.EnableViewState = true;
var prvCell = new TableCell();
var nextCell = new TableCell();
prvCell.Controls.Add(prvLink);
nextCell.Controls.Add(nextLink);
Table pagerTable = e.Row.Controls[0].Controls[0] as Table;
TableRow row = pagerTable.Rows[0];
row.Cells.AddAt(0, prvCell);
row.Cells.AddAt(row.Cells.Count, nextCell);
if (grid.PageIndex == 0)
{
prvCell.Enabled = false;
}
if (grid.PageIndex == grid.PageCount - 1)
{
nextCell.Enabled = false;
}
}
}
}
then I should be able to call a code like that in my page load and it should create the link buttons and response to click of them
var g = new GridStyler(CustomerGridView);
g.AddNextPreviousOnPager();
what happens is the link buttons are created just fine but when user clicks them page get refreshed but RowCommand never get fired (they get fired of course when user clicks other buttons but not this two dynamically created buttons)
Any suggestion is really appreciated

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