I'm creating dynamically controls, assigning it at the same dynamically names and ID's but when I click over a button "A" and then over the button "B" and then again to the button "A" this throw me an error
Multiple controls with the same ID were found. FindControl requires that controls have unique IDs.
this is my code and how I try to avoid the repeating I
protected void DynamicButton()
{
//BAD TOOLS INTO THE LIST AND SHOW
List.ListUsers listsArea = new List.ListUsers();
List<Data.Area> Area = listsArea.AreaList();
List<Data.Area> ListOfEquiposNoOk = Area.Where(x => x.AREA == "ENG" && x.STANDBY == 1).ToList();
List<Button> BotonesBad = new List<Button>();
var TeamBad = ListOfEquiposNoOk.Select(x => x.TEAM).Distinct().ToList();
foreach (var team in TeamBad)
{
Button newButtonBad = new Button();
if (newButtonBad.ID != newButtonBad.ID)
{
BotonesBad = Bad.Controls.OfType<Button>().ToList();
BotonesBad.Add(newButtonBad);
}
else
{
newButtonBad.CommandName = "Btn" + Convert.ToString(team);
newButtonBad.ID = "BtnB_" + Convert.ToString(team);
newButtonBad.Text = team;
newButtonBad.CommandArgument = "ENG";
newButtonBad.Click += new EventHandler(newButton_Click);
Bad.Controls.Add(newButtonBad);
newButtonBad.Click += new EventHandler(newButton_Click);
newButtonBad.CssClass = "btn-primary outline separate";
}
}
I need the ID's to fire an UpdatePanel
ADDED
public partial class Dashboard : System.Web.UI.Page
{
static bool enableGood = false;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
DynamicButton();
}
else if(enableGood)
{
DynamicButton();
}
}
protected void DButton(object sender, EventArgs e)
{
ScriptManager.RegisterStartupScript(this, this.GetType(), "Pop", "showAndHide();", true);
enableGood = true;
DynamicButton();
}
Assuming "team" is an object in this line:
newButtonBad.ID = "BtnB_" + Convert.ToString(team);
There could be your problem. ".ToString()" usually returns the type as string. So every button will get the same name (since they are all of type "team").
You could override ToString() in your team object, or use a specific team property (e.g team.ID). You could use the property like this:
newButtonBad.ID = "BtnB_" + team.ID.ToString();
Also as already pointed out in the comments, change your evaluation from
if (newButtonBad.ID != newButtonBad.ID)
to
if (team.ID != newButtonBad.ID)
That should do the trick.
Related
I'm reading a list of id numbers from a database table into a placeholder textbox but however; if I do a button click the data is removed.
protected void btnSearch_Click(object sender, EventArgs e)
{
while (myReader.Read())
{
TextBox txt = new TextBox();
txt.Text = (string)myReader["idNumber"];
txt.ID = "txt" + i;
txt.ReadOnly = true;
ContentPlaceHolder1.Controls.Add(txt);
ContentPlaceHolder1.Controls.Add(new LiteralControl(" "));
i++;
}
}
This is a common problem when working with dynamically added controls in web forms (especially if you're coming from a winforms background). Pages in ASP.NET Web Forms are stateless, and reconstructed on each postback. Therefore, if you add a control to a page during a server event, you'll also have to add it to the page on subsequent Page loads if you want it to appear. You can accomplish this with something similar to the following:
protected List<Control> ControlCache
{
get => (List<Control>)(Session["cachedControlsForPageX"] = (Session["cachedControlsForPageX"] as List<Control>) ?? new List<Control>());
set => Session["cachedControlsForPageX"] = value;
}
/* If you can't use C# 7's expression bodied property accessors, here's the equivalent in blocks:
protected List<Control> ControlCache
{
get { return (List<Control>)(Session["cachedControlsForPageX"] = (Session["cachedControlsForPageX"] as List<Control>) ?? new List<Control>()); }
set { Session["cachedControlsForPageX"] = value; }
}
*/
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
foreach (var control in ControlCache)
{
ContentPlaceHolder1.Controls.Add(control);
ContentPlaceHolder1.Controls.Add(new LiteralControl(" "));
}
}
else
ControlCache = null;
}
protected void btnSearch_Click(object sender, EventArgs e)
{
while (myReader.Read())
{
TextBox txt = new TextBox();
txt.Text = (string)myReader["idNumber"];
txt.ID = "txt" + i;
txt.ReadOnly = true;
ContentPlaceHolder1.Controls.Add(txt);
ContentPlaceHolder1.Controls.Add(new LiteralControl(" "));
ControlCache.Add(txt);
i++;
}
}
First time poster, long time lurker. I am having some trouble with my ASP.NET page, and I hope someone can help me resolve my issue.
Basically, I have a bunch of checkboxes in a gridview, and two buttons: a 'find' button, and a 'save' button. The 'find' can set the value of the checkbox, but if a user unchecks it, I want to capture that change when the user hits 'save'. Currently, it does not work.
Relevant ASPX:
<%# Page Language="C#" AutoEventWireup="true" EnableViewState="true" CodeBehind="FindTransactions.aspx.cs" Inherits="Basic.FindTransactions" MasterPageFile="~/Trans.Master" %>
Relevant Code Behind here:
Page:
public partial class FindTransactions : System.Web.UI.Page
{
GridView _gridview = new GridView() { ID = "_gridView" };
DataTable _datatable = new DataTable();
Int32 _buyerID = new Int32();
protected void Page_Load(object sender, EventArgs e)
{
}
"Find" button:
protected void Find_Click(object sender, EventArgs e)
{
//truncated
_datatable.Rows.Add(
//filled with other data from a custom object.
);
ViewState["_datatable"] = _datatable;
ViewState["_buyerID"] = _buyerID;
BuildGridView((DataTable)ViewState["_datatable"],(Int32)ViewState["buyerID"]);
}
BuildGridView function:
protected void BuildGridView(DataTable d, Int32 b)
{
_gridview.DataKeyNames = new String[] {"Transaction ID"};
_gridview.AutoGenerateColumns = false;
_gridview.RowDataBound += new GridViewRowEventHandler(OnRowDataBound);
for(Int32 i = 0; i < d.Columns.Count; i++)
{
Boundfield boundfield = new BoundField();
boundfield.DataField = d.Columns[i].ColumnName.ToString();
boundfield.HeaderText = d.Columns[i].ColumnName.ToString();
_gridview.Columns.Add(boundfield);
}
_gridview.DataSource = d;
_gridview.DataBind();
//truncated
Panel1.Controls.Add(_gridview);
}
Row Bound Event handler:
protected void OnRowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
String controlID = "checkBox";
CheckBox c = new CheckBox() { ID = controlID};
c.Enabled = true;
Boolean success;
Boolean v;
success = Boolean.TryParse(e.Row.Cells[8].Text, out v);
e.Row.Cells[8].Controls.Add(c);
if (success)
{
c.Checked = v;
if (c.Checked)
{
//Will uncomment once other things work
//e.Row.Visible = false;
}
}
else
{
c.Checked = false;
}
}
}
All of that works. Here is where it starts to break down:
"Save" button:
protected void Save_Click(object sender, EventArgs e)
{
//Both for troubleshooting and both return 0. (Expected for datatable)
Label1.Text = _gridview.Rows.Count.ToString();
Label2.Text = _datatable.Rows.Count.ToString();
/*truncated
*/
if (grid.Rows.Count == 0)
{
BuildGridView((DataTable)ViewState["infoTable"], (Int32)ViewState["guestID"]);
}
foreach (GridViewRow r in grid.Rows)
{
if (r.RowType == DataControlRowType.DataRow)
{
CheckBox cb = (CheckBox)r.FindControl("checkBox");
if (cb != null && cb.Checked)
{
//This never seems to modify the label.
//Will put code to modify database here.
Label2.Text += "Hi " + r.RowIndex.ToString();
}
}
}
}
After I hit the save button, PostBack occurs and GridView is empty (Rows.Count is 0). ViewState appears to be lost before I get a chance to loop through the GridView rows to determine the checkbox values.
At the end of it all, I just want to capture the status of those checkboxes, changed by user interaction or not, by hitting the 'Save' button.
I found some other articles, but a lot of them haven't worked when I tried implementing the various fixes.
This one seems to be the closest that describes my issue, and the code is structured similarly, but I don't quite understand how to implement the fix: GridView doesn't remember state between postbacks
[New simplified code to illustrate problem:]
namespace GridViewIssue
{
public partial class GridViewNoMaster : System.Web.UI.Page
{
GridView _gridView = new GridView() { ID = "_gridView" };
DataTable _dataTable = new DataTable();
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Find_Click(object sender, EventArgs e)
{
BuildDataTable();
List<String> list = new List<String>();
list.Add("1");
list.Add("User");
list.Add("10/12/2014");
foreach (String s in list)
{
_dataTable.Rows.Add(
list[0],
list[1],
list[2]
);
}
BuildGridView();
//Feedback.Text = _gridView.Rows.Count.ToString();
}
protected void Save_Click(object sender, EventArgs e)
{
Feedback.Text = "Save Clicked, PostBack: " + IsPostBack + ", GridView Row Count: " + _gridView.Rows.Count + ", GridView ViewState: " + _gridView.EnableViewState;
foreach (GridViewRow r in _gridView.Rows)
{
if(r.RowType == DataControlRowType.DataRow)
{
Feedback.Text = "In DataRow type" + _gridView.Rows.Count;
}
}
}
protected void BuildDataTable()
{
_dataTable.Columns.Add("Transaction ID", typeof(String));
_dataTable.Columns.Add("Name", typeof(String));
_dataTable.Columns.Add("Date", typeof(String));
}
protected void BuildGridView()
{
for (Int32 i = 0; i < _dataTable.Columns.Count; i++)
{
BoundField b = new BoundField();
b.DataField = _dataTable.Columns[i].ColumnName.ToString();
b.HeaderText = _dataTable.Columns[i].ColumnName.ToString();
_gridView.Columns.Add(b);
}
_gridView.DataKeyNames = new String[] { "Transaction ID" };
_gridView.AutoGenerateColumns = false;
_gridView.DataSource = _dataTable;
_gridView.DataBind();
Panel1.Controls.Add(_gridView);
}
}
}
I have this Module in my project in which I have 2 gridviews. One is for the Main MenuModule and the other one is for it's subMenu. I created a List so that when a row on my Main Menu Module has been checked and it has a corresponding submenu, it will show on the SubMenu Gridview.
Now, I can see my SubMenuGridview when I get back to that page (I used session), but I noticed that the checkbox I ticked were all gone.
My problem was on how can my page remember the checkboxes I checked, both from my Main Menu Module gridview and from my Submenu gridview.
protected void cbxSelect_CheckedChanged(object sender, EventArgs e)
{
SubMenuGrid.DataSource = null;
SubMenuGrid.DataBind();
Business.SubMenuModules sub = new Business.SubMenuModules();
List<oSubList> oList = new List<oSubList>();
int counter = 0;
foreach (GridViewRow nRow in gvModuleList.Rows)
{
Int32 intModID = Convert.ToInt32(nRow.Cells[0].Text);
CheckBox chkBx = (CheckBox)nRow.FindControl("cbxSelect");
if (chkBx.Checked == true)
{
counter = counter + 1;
var oModList = sub.GetAllMenuPerModuleID(intModID);
if (oModList.Count > 0)
{
foreach (var rec in oModList)
{
oSubList olist = new oSubList
{
ID = rec.ID,
ModuleID = rec.ModuleID,
Submenu = rec.Submenu,
Description = rec.Description
};
oList.Add(olist);
}
Session["list"]=oList;
SubMenuGrid.DataSource = oList;
SubMenuGrid.DataBind();
}
}
}
}
This can be done using:
ViewState
SessionPageStatePersister
Custom Session based solution
For view state-see the below posted link in comments..
Custom Session based solution
We are going to use the pre render method.
This method is being invoked after the page has been initialized but before it saved its ViewState and rendered.
Are are going to load the Request.Form into a session variable and load it back with each call to the page that is not a postback.
protected void Page_PreRender(object sender, EventArgs e)
{
if (!Page.IsPostBack && Session["PageState"] != null)
{
NameValueCollection formValues = (NameValueCollection)Session["PageState"];
String[] keysArray = formValues.AllKeys;
for (int i = 0; i < keysArray.Length; i++)
{
Control currentControl = Page.FindControl(keysArray[i]);
if (currentControl != null)
{
if (currentControl.GetType() == typeof(System.Web.UI.WebControls.TextBox)) ((TextBox)currentControl).Text = formValues[keysArray[i]];
else if (currentControl.GetType() == typeof(System.Web.UI.WebControls.CheckBox))
{
if (formValues[keysArray[i]].Equals("on")) ((CheckBox)currentControl).Checked = true;
}
else if (currentControl.GetType() == typeof(System.Web.UI.WebControls.DropDownList))
((DropDownList)currentControl).SelectedValue = formValues[keysArray[i]].Trim();
}
}
}
if(Page.IsPostBack) Session["PageState"] = Request.Form;
}
SessionPageStatePersister
The abstract PageStatePersister class represents the base class that encapsulates the Page State storage and processing.
Storage can be done with the default HiddenFieldPageStatePersister class or with SessionPageStatePersister.
When SessionPageStatePersister is used, the .NET Server manages the _VIEWSTATE in a session object instead of a hidden field on your form.
Changing the state storage looks like this:
protected override PageStatePersister PageStatePersister
{
get
{
return new SessionPageStatePersister(this);
}
}
Here i am updating the gridview value but the value is not updating..TextBox txtID,TextBox txtName,TextBox txtAge retains the older value only and the value is not getting updated..Can anyone tel me like what am i doing wrong here
Here is my code
protected void gvTemp_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
CreateDataSkeletton(Convert.ToInt16(Session["intTabIndex"]));
GridViewRow row = (GridViewRow)gvTemp.Rows[e.RowIndex];
int autoid = Int32.Parse(gvTemp.DataKeys[e.RowIndex].Value.ToString());
int id = Convert.ToInt32(gvTemp.DataKeys[e.RowIndex].Values[0].ToString());
activeTabIndex = Convert.ToInt16(Session["activeTabIndex"]);
TextBox txtID = ((TextBox)gvTemp.Rows[e.RowIndex].FindControl("txtId"));
TextBox txtName = (TextBox)gvTemp.Rows[e.RowIndex].FindControl("txtName");
TextBox txtAge = (TextBox)gvTemp.Rows[e.RowIndex].FindControl("txtAge");
dataSetInSession.Tables["Days" + activeTabIndex.ToString()].Rows[e.RowIndex]["ID"] = txtID.Text;
dataSetInSession.Tables["Days" + activeTabIndex.ToString()].Rows[e.RowIndex]["Name"] = txtName.Text;
dataSetInSession.Tables["Days" + activeTabIndex.ToString()].Rows[e.RowIndex]["Age"] = txtAge.Text;
gvTemp.DataSource = dataSetInSession.Tables["Days" + activeTabIndex.ToString()];
gvTemp.DataBind();
gvTemp.EditIndex = -1;
}
and
private void CreateDataSkeletton(int intTabIndex)
{
dataSetInSession = new DataSet();
Session["intTabIndex"] = intTabIndex;
if (Session["dataSetInSession"] != null)
{
dataSetInSession = (DataSet)Session["dataSetInSession"];
}
if (dataSetInSession.Tables["Days" + intTabIndex].Rows.Count > 0)
{
gvTemp.DataSource = dataSetInSession.Tables["Days" + intTabIndex];
gvTemp.DataBind();
}
else
{
gvTemp.DataSource = dataSetInSession.Tables["Days"];
gvTemp.DataBind();
}
temp.Controls.Add(gvTemp);
}
Any suggestion??
EDIT(1):
protected void Page_Init(object sender, EventArgs e)
{
if (!IsPostBack)
{
AddDataTable();
}
dataSetInSession = new DataSet();
if (Session["dataSetInSession"] != null)
{
dataSetInSession = (DataSet)Session["dataSetInSession"];
}
if (Session["dynamicTabIDs"] != null)
{
//if dynamicTabIDs are in session, recreating the Tabs
//that are associated with the Tab IDs
//and adding them to the TabContainer that will contain
//all of the dynamic tabs.
//retrieving the tab IDs from session:
dynamicTabIDs = (List<string>)Session["dynamicTabIDs"];
if (TabContainerContent.ActiveTabIndex == -1)
{
TabContainerContent.ActiveTabIndex = Convert.ToInt16(Session["intTabIndex"]);
}
CreateDataSkeletton(Convert.ToInt16(Session["intTabIndex"]));
//looping through each TabID in session
//and recreating the TabPanel that is associated with that tabID
foreach (string tabID in dynamicTabIDs)
{
//creating a new TabPanel that is associated with the TabID
AjaxControlToolkit.TabPanel tab = new AjaxControlToolkit.TabPanel();
//TabContainerContent.ActiveTabIndex = tab;
//Setting the ID property of the TabPanel
tab.ID = tabID;
//setting the TabPanel's HeaderText
tab.HeaderText = "Days " + (TabContainerContent.Tabs.Count + 1).ToString();
//creating a Label to add to the TabPanel...at this point you'll have to
//create whatever controls are required for the tab...
Label tabContent = new Label();
//Giving the Label an ID
tabContent.ID = "lbl_tab_" + TabContainerContent.Tabs.Count.ToString();
//Setting the Label's text
tabContent.Text = "Tab " + (TabContainerContent.Tabs.Count + 1).ToString();
//Adding the Label to the TabPanel
tab.Controls.Add(tabContent);
//Adding the TabPanel to the TabContainer that contains the dynamic tabs
TabContainerContent.Tabs.Add(tab);
}
}
else
{ //Creating a new list of dynamicTabIDs because one doesn't exist yet in session.
dynamicTabIDs = new List<string>();
}
}
protected void Page_Load(object sender, EventArgs e)
{
}
Read Page Life Cycle
And you will get, that if you want to bind in code-behind, you should do it in Init Event, other wise there are no events will fire.
It seems to be Page.IsPostback problem.Need to add Page.IsPostback property in your page_load like
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
// Put your code inside that.
}
}
Actually your control is getting new value but when you click for updation then it calls old value from page_load.So try to put Page.IsPostback into your page_load event,Just like i mentioned.
I hope it helps.
I have a web part for managing comments related to ongoing promotions. The web part is hosted in a Sandbox Solution because server access of all kinds is restricted (//sharepoint)
I have two main issues with my code.
1: Items submitted do not appear after postback, leaving user to think their comments was not saved,
2: PostBack data refires after page refresh, meaning if a user refreshes hoping to see their comments, it is re-submitted and saved.
What am I doing wrong here?
public string OfferID { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
OfferID = Context.Request.QueryString["ItemID"];
LoadOffers();
}
protected void LoadOffers()
{
if (!String.IsNullOrEmpty(OfferID))
{
PopulateOfferDetails(OfferID);
PopulateComments(OfferID);
PopulateBestPractices(OfferID);
}
else
{
OfferID = "123";
PopulateOfferDetails(OfferID);
PopulateComments(OfferID);
PopulateBestPractices(OfferID);
}
}
protected void PopulateComments(string offerID)
{
rcOiD.InnerText += " " + offerID;
List<Comment> Comments = new List<Comment>();
SPList TargetList = web.Lists.TryGetList("Offer Comments");
SPQuery query = new SPQuery();
query.RowLimit = 100;
query.Query = "<Where><Eq><FieldRef Name=\"OfferID\"/><Value Type=\"Text\">" + offerID + "</Value></Eq></Where>";
try
{
SPListItemCollection items = TargetList.GetItems(query);
if (items.Count > 0)
{
commentsCount.InnerText = items.Count.ToString();
SPUser user = web.CurrentUser;
string alias = user.Email.Substring(0, user.Email.IndexOf('#'));
string profilePicBase = "<div class=\"profilePic\" " + "style=\"background-image:url('http://who/Photos/XX.jpg');\"" + "> </div>";
foreach (SPListItem item in items)
{
Comment c = new Comment();
c.Author = ((string)item["Created By"]).CleanUserName();
c.Body = (string)item["Body"];
c.Date = String.Format("{0:MMM dd, yyyy}", (DateTime)item["Created"]);
c.ProfilePic = profilePicBase.Replace("XX", alias);
Comments.Add(c);
}
Comments.Reverse();
CommentRepeater.DataSource = Comments;
CommentRepeater.DataBind();
}
else
{
commentsCount.InnerText = "0";
}
}
catch (Exception ex)
{
}
}
protected void SubmitListItem(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
SPUser user = web.CurrentUser;
string alias = user.Email.Substring(0, user.Email.IndexOf('#'));
if (ListChoice.SelectedItem.Text == "comment")
{
SPList TargetList = web.Lists.TryGetList("Offer Comments");
SPListItem item = TargetList.Items.Add();
item["Title"] = TitleBox.Text;
item["Body"] = BodyBox.Text;
item["OfferID"] = OfferID;
item["Alias"] = alias;
item.SystemUpdate();
TargetList.Update();
}
else
{
SPList TargetList = web.Lists.TryGetList("Offer Best Practices");
SPListItem item = TargetList.Items.Add();
item["Title"] = TitleBox.Text;
item["Body"] = BodyBox.Text;
item["OfferID"] = OfferID;
item.SystemUpdate();
TargetList.Update();
}
}
}
EDIT: I can confirm this isn't a databind() issue. The item.count being pulled on postback is being rendered properly, but is still 1 item short.
I assume SubmitListItem is an event handler of an control on the page.
If that is so then as in your previous question, Page_Load is is fired before any control's event handler.
Therefore on postback your repeater is getting bound before the item addition occurs so on that load you do not get to see the new item.
To prevent this rebind the repeater after item addition.
I think you should do this only if not is page postback:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
OfferID = Context.Request.QueryString["ItemID"];
LoadOffers();
}
}