Click to See Complete Forum and Search --> : Remove element from ArrayList through autogenerated Button


codeme
November 27th, 2008, 08:47 AM
Hello,
I need to code a project for school in c#, everything was going smoothly, till i tried to do the following:
I have an ArrayList of objects, I want to generate Buttons which, when i click it, deletes the corresponding item in the ArrayList, sounds pretty straight forward, right?
So I started coding, it didn't really went as smoothly as planned, so I started coding it seperately in a testproject, where it still didn't work... For some reason, the following code works, but only if i click the button twice, which obviously isn't the plan.

WebForm1.cs

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;

namespace programmerenproject
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ArrayList list;

if (!IsPostBack)
{
list = new ArrayList();
list.Add("test1");
list.Add("test2");
list.Add("test3");
list.Add("test4");
ViewState["list"] = list;
generateButtons(list);
}
else
{
list = (ArrayList)ViewState["list"];
generateButtons(list);
}
}

private void ClickMe(object sender, System.EventArgs e)
{
ArrayList list = (ArrayList)ViewState["list"];

MyButton who = (MyButton)sender;
list.RemoveAt(who.Element);
lblTest.Text = list[who.Element].ToString();
generateButtons(list);

ViewState["list"] = list;
}

public void generateButtons(ArrayList l)
{
pnlTest.Controls.Clear();
int number = l.Count;
MyButton[] myButtons = new MyButton[number];
for (int i = 0; i < number; i++)
{
myButtons[i] = new MyButton(i);
myButtons[i].Text = l[i].ToString();
myButtons[i].Click += new System.EventHandler(this.ClickMe);
pnlTest.Controls.Add(myButtons[i]);
}
}
}


}

MyButton.cs (extends Button so I can add some extra data behind the button)

using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Collections;

namespace programmerenproject
{
public class MyButton:Button
{
int element;

public int Element
{
get { return element; }
set { element = value; }
}

public MyButton(int e)
{
element = e;
}

}
}


WebForm1.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="programmerenproject.WebForm1" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>

<asp:Panel ID="pnlTest" runat="server">
</asp:Panel>
<asp:Label ID="lblTest" runat="server"></asp:Label>

<br />

</div>
</form>
</body>
</html>



I hope someone can point out my problem... or give me a pointer in the right direction :)

JonnyPoet
November 28th, 2008, 05:40 PM
There are different problems !
One I have solved for you is that it doesn't show you the correct item you have deleted
public partial class _Default : System.Web.UI.Page
{
private ArrayList list;
protected void Page_Load(object sender, EventArgs e)
{

if (!IsPostBack)
{
list = new ArrayList();
list.Add("test1");
list.Add("test2");
list.Add("test3");
list.Add("test4");
ViewState["list"] = list;
generateButtons(list);
}
else
{
list = (ArrayList)ViewState["list"];
generateButtons(list);
}
}
private void ClickMe(object sender, System.EventArgs e)
{
MyButton who = (MyButton)sender;
// now we show which button is removed
lblTest.Text = list[who.Element].ToString();
// we remove the button whoms text is shown
list.RemoveAt(who.Element);
// now we have one button less so the listindex are changed
generateButtons(list);
ViewState["list"] = list;
}
public void generateButtons(ArrayList l)
{
pnlTest.Controls.Clear();
int number = l.Count;
MyButton[] myButtons = new MyButton[number];
for (int i = 0; i < number; i++)
{
myButtons[i] = new MyButton(i);
myButtons[i].Text = l[i].ToString();
myButtons[i].Click += new System.EventHandler(this.ClickMe);
pnlTest.Controls.Add(myButtons[i]);
}
}
}As you see I'm using a private field for your Arraylist so I didn't need to load it from the viewstate when the click event is called.
The othe problem simple has to do with the fact that the index changes when you remove an item in a list. So you need to show its name in the label before you remove it !! Or you will get a wrong item when doing after deletion Use the debugger and you will see this.

The problem with not getting every click i havn't solved in between.
But why nor simple using an repeater for adding/removing buttons dynamically :D

----- edited ------
look at my next post for a full solution of your problems.

JonnyPoet
November 30th, 2008, 02:22 PM
Hi !
I have done a bit more research and here is the working codeusing System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;
public partial class _Default : System.Web.UI.Page {
private List<Button> list;
private ArrayList txtList;
protected void Page_Load(object sender, EventArgs e) {
if (!IsPostBack) {
txtList = new ArrayList();
txtList.Add("Wert1");
txtList.Add("Wert2");
txtList.Add("Wert3");
txtList.Add("Wert4");
txtList.Add("Wert5");
txtList.Add("Wert6");
generateButtons(txtList);
Session.Add("list", txtList);
}
}
private void ClickMe(object sender, System.EventArgs e) {
Button who = (Button)sender;
// now we show which button is removed
lblTest.Text = who.Text.ToString();
// we remove the button whoms text is shown
txtList.Remove(lblTest.Text);
// now we have one button less so the listindex are changed
generateButtons(txtList);
Session["list"] = txtList;
}
public void generateButtons(ArrayList lt) {
list = new List<Button>();
pnlTest.Controls.Clear();
pnlTest.EnableViewState = true;
int number = lt.Count;
for (int i = 0; i < number; i++) {
Button myButton = new Button();
// we need to make them different ID's so they work without any problems
myButton.ID = "DynButton" + (i + 1).ToString();
myButton.Text = lt[i].ToString();
//myButton.EnableViewState = true;
myButton.Click += new System.EventHandler(this.ClickMe);
list.Add(myButton);
pnlTest.Controls.Add(myButton);
}
}
/// <summary>
/// We generate dynamic controls here ! See MSDN reference attached
/// </summary>
/// <param name="e"></param>
protected override void OnPreInit(EventArgs e) {
if (IsPostBack) {
txtList = (ArrayList)Session["list"];
generateButtons(txtList);
}
base.OnPreInit(e);
}
}
// And the aspx Page is like this:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>

<asp:Panel ID="pnlTest" runat="server" EnableViewState="true" >
</asp:Panel>
<asp:Label ID="lblTest" runat="server"></asp:Label>

<br />

</div>
</form>

</body>
</html>
I havn't used any namespace and also I was using standard buttons.
But it will work with your buttons in the same way

And here is a reference why we do it this way.
http://msdn.microsoft.com/en-us/library/ms178472.aspx

Especially look on when to generate the controls after a postback. Its done in OnPreInit as you can see at MSDN. Setting different ID's lets your Controls delegates work. It will not work properly without them. Try it, debug it and you will see it works Every hit to a button also generates an event that could be catched by the correct event handler.